© Copyright 2018 – 2024 FIRECRACKER SPORTS. All Rights Reserved.​ TERMS OF USE  |  PRIVACY POLICY

18th Annual 4th of July Showcase Tournament – Baseball

06/28/2025 - 07/02/2025
Providence ,

INFORMATION

  • Age groups:
    6 Divisions 13U 14U 15U 16U 17U 18/19U
  • Pool Play:
    Saturday, June 28th through Tuesday, July 1st, 2025
  • Price:
    13U - $189914U - $189915U - $189916U - $189917U - $189918/19U - $1899
  • Playoffs/Championships:
    Wednesday, July 2nd, 2025

 

  • 4 Game Guarantee: Pool play format will be used with all teams entered (weather permitting).
  • 204 teams maximum for this event
  • Registration Fee: $1,899.00
  • Baseballs and lineup cards are included for each team for this event.
  • Bats: Check Rules tab above.
  • Two (2) High School board certified Umpires are supplied for every game.
  • Preliminary round games will take place at area town, high school and collegiate fields with the playoffs and championship games at college facilities
  • NO GATE FEES EVER!
  • Above and Beyond our Competition...Expanded Social Media Coverage included during the entirety of this event. Our digital content team promotes players and teams on our various social media platforms, conducts pre and post game interviews with players and coaches, captures photographs and videos during the event and reports the days activity all on our website! Also included are live in game scores, stats and standings for everyone to follow along on our website and app!
  • All teams are subject to play at any time at the tournament director's request.

Full payment is due on February 1, 2025. Your registration into this tournament is not guaranteed or secured until full payment is received. Teams who have registered and have not paid by February 1, 2025 are subject to being replaced by another fully paid registrant.

Registration

  • DIVISION:
    • 13U
    • 14U
    • 15U
    • 16U
    • 17U
    • 18/19U
    • OPEN
    • OPEN
    • OPEN
    • OPEN
    • OPEN
    • OPEN
    211 Teams Registered
    COST: $1899

College Coaches Attending

 

  1. University of Pittsburgh
  2. University of Missouri
  3. Bryant University
  4. Brown University
  5. University of Rhode Island
  6. University of Albany
  7. Central Connecticut State University
  8. Assumption University
  9. University of Bridgeport
  10. Bentley University
  11. Castleton State University
  12. Southern Connecticut State University
  13. Albertus Magnus College
  14. Rhode Island College
  15. University Of St. Joseph's (CT)
  16. Rensselaer Polytechnic Institute
  17. Massasoit Community College
  18. Johnson And Wales University
  19. Rhode Island College
  20. Clark University
  21. Nichols College
  22. Curry College
  23. Massachusetts Maritime Academy
  24. Coast Guard Academy
  25. Eastern Connecticut State University
  26. Salve Regina University
  27. Roger Williams University
  28. Hudson Valley Community College
  29. Bryant And Stratton University
  30. St. Lawrence University

Teams

(23 teams) - OPEN
(35 teams) - OPEN
(38 teams) - OPEN
(36 teams) - OPEN
(39 teams) - OPEN
(26 teams) - OPEN

Locations

Bishop Hendricken High School

2615 Warwick Avenue

Warwick rhode island , United States

Get Directions
VENUE DIVISIONS
  • 15U
Fields
  • Baseball Field
    • Grass
    • Permanent Bathrooms
Brown University

235 Hope Street

Providence rhode island

Get Directions
VENUE DIVISIONS
  • 17U
Fields
  • Attanasio Family Field at Murray Stadium
    • Turf
    • Permanent Bathrooms
    • Portable Bathrooms
  • Erickson Athletic Complex - Softball Field
    • Turf
    • Permanent Bathrooms
    • Portable Bathrooms
VENUE DETAILS

Metal spikes along with sunflower seeds are not allowed on the campus fields. 

Also parking is extremely limited on campus, so off-site and street parking is only allowed. Customers will be ticketed, if parked in campus spots not permitted. 

Bryant University

1150 Douglas Pike

Smithfield rhode island , United States

Get Directions
VENUE DIVISIONS
  • 16U
Fields
  • Conaty Park
    • Turf
    • Portable Bathrooms
  • Bryant Softball Complex
    • Grass
    • Portable Bathrooms
VENUE DETAILS

Conaty Park is a turf collegiate baseball field . There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

Softball Complex is not a turf field as indicated on venue list. 

Calise Field

625 Dyer Ave

Cranston rhode island , United States

Get Directions
VENUE DIVISIONS
  • 13U
Fields
  • Calise Field
    • Grass
    • Portable Bathrooms
Campanelli Stadium

1 Feinberg Way

Brockton massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 17U
Fields
  • Campanelli Stadium
    • Lights
    • Grass
    • Permanent Bathrooms
VENUE DETAILS

This is a pro stadium so please be courteous of amenities and parking on site. 

Coast Guard Academy

47 Mohegan Ave

New London connecticut

Get Directions
VENUE DIVISIONS
  • 18/19U
Fields
  • Coast Guard Academy
    • Grass
    • Portable Bathrooms
VENUE DETAILS

All visitors arriving to campus must present a Real ID, as the United States Coast Guard Academy is a military base. Please remind your families about the REAL ID requirement.  More information can be found on the Academy’s website: www.uscga.edu including acceptable forms of alternative ID.

Finally, and most importantly, please communicate to the families that access to the campus on 6/30 will be through the Rowing Center Gate.  

This is because 6/30 is our first day of boot camp or Reporting-In Day.  The Academy is trying to keep the two groups separate and the lines through both gates moving quickly.  Access to upper campus will be blocked off for the day.  I’ve attached a map for clarification.  

On 7/1, whole campus access will be available again, and campus entry will once again be routed through the front gate (off of Route 32).

Cranston Stadium

20 Jordan Avenue

Cranston rhode island , United States

Get Directions
VENUE DIVISIONS
  • 15U
Fields
    • Lights
    • Grass
    • Portable Bathrooms
Cranston West High School

80 Metropolitan Ave

Cranston rhode island , United States

Get Directions
VENUE DIVISIONS
  • 14U
Fields
    • Grass
    • Portable Bathrooms
Cumberland High School

2600 Mendon Rd

Cumberland rhode island , United States

Get Directions
VENUE DIVISIONS
  • 14U
Fields
  • Tucker Field
    • Grass
    • Portable Bathrooms
VENUE DETAILS

Parking is allowed near the tennis courts as soon as you pull into the Andrew Tucker athletic complex. Parking is NOT allowed next to the Sher-la-mon swim club, but you can park in the gravel parking lot next to the swim club. 

Curry College

102 Atherton Street

Milton rhode island , United States

Get Directions
VENUE DIVISIONS
  • 16U
  • 18/19U
Fields
  • Jack Vallely Baseball Diamond
    • Grass
    • Portable Bathrooms
Dean College

69 Maple Street

Franklin massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 16U
Fields
  • Longley Athletic Complex
    • Grass
    • Portable Bathrooms
VENUE DETAILS

Athletic fields are located at the Longley Athletic Complex, a short drive from Dean College's campus. Athletic fields are not on campus. 

East Providence High School

2000 Pawtucket Ave

East Providence rhode island

Get Directions
VENUE DIVISIONS
  • 17U
Fields
    • Lights
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf high school field . There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

Eastern Connecticut State University

1 Mansfield City Rd

Willimantic connecticut , United States

Get Directions
VENUE DIVISIONS
  • 17U
Fields
  • Eastern Baseball Stadium
    • Lights
    • Grass
    • Permanent Bathrooms
Fairlawn Veterans Memorial Park

271 Smithfield Ave

Pawtucket rhode island , United States

Get Directions
VENUE DIVISIONS
  • 13U
Fields
  • Fairlawn Field A
    • Lights
    • Grass
    • Portable Bathrooms
Massachusetts Maritime Acadmey

101 Academy Drive

Buzzards Bay massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 16U
Fields
  • Commodore William R Hendry Field
    • Lights
    • Turf
    • Permanent Bathrooms
    • Portable Bathrooms
VENUE DETAILS

This is a turf collegiate field. There is a strict policy on no sunflower seeds and metal spikes allowed. Any violations of this policy will result in the player and/or team's remove from the event for it's duration. 

Mitchell College

437 Pequot Ave

New London connecticut , United States

Get Directions
VENUE DIVISIONS
  • 16U
  • 17U
  • 18/19U
Fields
  • Alumni Field
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf collegiate field. There is a strict policy on no sunflower seeds, gum and metal spikes allowed. Any violations of this policy will result in the player and/or team's remove from the event for it's duration. 

Mount Pleasant High School

434 Mount Pleasant Ave

Providence rhode island

Get Directions
VENUE DIVISIONS
  • 15U
Fields
  • Limmina Field
    • Grass
    • Portable Bathrooms
Norfolk Agricultural High School

400 Main Street

Walpole massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 13U
  • 16U
Fields
    • Grass
    • Portable Bathrooms
North Providence High School

1828 Mineral Spring Ave

North Providence rhode island , United States

Get Directions
VENUE DIVISIONS
  • 14U
Fields
  • Lee Romano Field
    • Lights
    • Turf
    • Grass
    • Permanent Bathrooms
VENUE DETAILS

This is a turf high school field. There is a strict policy on no sunflower seeds and metal spikes allowed. No alcohol allowed on the premises as this is a public school campus. Any violations of this policy will result in the player and/or team's remove from the event for it's duration. 

Pierce Memorial Stadium

201 Mercer Street

East Providence rhode island , United States

Get Directions
VENUE DIVISIONS
  • 15U
Fields
  • W.B. Pierce Field
    • Lights
    • Grass
    • Permanent Bathrooms
Plymouth South High School

490 Long Pond Road

Plymouth massachusetts , US

Get Directions
VENUE DIVISIONS
  • 13U
Fields
  • Edward C. Swenson Athletic Complex
    • Lights
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf high school field . There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

Pope Park

Pope Street

Acushnet massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 15U
Fields
  • 60/90 Field
    • Lights
    • Grass
    • Portable Bathrooms
  • 50/70 Field
    • Grass
  • 46/60 Field
    • Grass
Rhode Island College

600 Library Road

Providence rhode island , United States

Get Directions
VENUE DIVISIONS
  • 18/19U
Fields
  • Pontarelli Field
    • Grass
    • Permanent Bathrooms
    • Concession Stand
  • Dayna A. Bazar Softball Complex
    • Lights
    • Grass
    • Permanent Bathrooms
    • Concession Stand
VENUE DETAILS

No dogs, tents or coolers are allowed in the Athletic venues or complex. 

Rice Complex

54 Emerald St

Wrentham massachusetts

Get Directions
VENUE DIVISIONS
  • 14U
Fields
  • Duffy Field
    • Grass
    • Portable Bathrooms
  • Bogardus Field
    • Grass
    • Portable Bathrooms
  • 60/90 Field
    • Grass
    • Portable Bathrooms
VENUE DETAILS

There are no dogs allowed at the Rice Complex. Please follow signs for parking.

Riverpoint Park

106 Hay St

West Warwick rhode island , USA

Get Directions
VENUE DIVISIONS
  • 13U
  • 15U
  • 16U
  • 17U
Fields
  • McCarthy Stadium
    • Lights
    • Grass
    • Permanent Bathrooms
    • Portable Bathrooms
  • Ray Silva Field
    • Lights
    • Grass
    • Permanent Bathrooms
    • Portable Bathrooms
Roger Williams University

1 Old Ferry Rd

Bristol rhode island , United States

Get Directions
VENUE DIVISIONS
  • 16U
  • 17U
Fields
  • Paolino Field
    • Grass
    • Permanent Bathrooms
    • Portable Bathrooms
VENUE DETAILS

Visitors can enter campus through the North Entrance and park in Lot D for the Baseball Field.

Salve Regina University

45 Shepard Avenue

Newport rhode island , United States

Get Directions
VENUE DIVISIONS
  • 17U
Fields
  • Brother Reynolds Field
    • Grass
    • Portable Bathrooms
VENUE DETAILS

No parking next to baseball field permitted. Parking must be in designated lots. Customer assumes all risk of parking on campus.

Slater Park

401 Newport Ave

Pawtucket rhode island

Get Directions
VENUE DIVISIONS
  • 14U
Fields
  • McConnon Field
    • Lights
    • Grass
    • Portable Bathrooms
  • Dick Cosimini Field
    • Grass
Sprague Memorial Complex

179 Kingston Road

Narraganset rhode island

Get Directions
VENUE DIVISIONS
  • 14U
Fields
  • Narragansett Little League baseball field
    • Lights
    • Grass
    • Portable Bathrooms
Strong Field

50 Strong Avenue

East Bridgewater massachusetts , United States

Get Directions
VENUE DIVISIONS
  • 14U
Fields
    • Grass
    • Permanent Bathrooms
Taunton High School

50 Williams St

Taunton massachusetts

Get Directions
VENUE DIVISIONS
  • 15U
Fields
  • Arthur "Buck" Mahoney Field
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf high school field . There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

UMass Boston at Monan Park

University Dr W

Boston massachusetts

Get Directions
VENUE DIVISIONS
  • 16U
  • 17U
Fields
  • Varsity Field
    • Lights
    • Turf
    • Portable Bathrooms
  • JV Field
    • Lights
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf collegiate complex and fields. There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

University of Rhode Island

3 Keaney Rd

Kingston rhode island , United States

Get Directions
VENUE DIVISIONS
  • 17U
  • 18/19U
Fields
  • Bill Beck Field
    • Turf
    • Portable Bathrooms
VENUE DETAILS

This is a turf collegiate field . There is a strict enforcement on no sunflower seeds, gum and metal spikes allowed. Any violations by a player or team and they will be removed from the event for it's duration. 

schedule

HOTELS

To Hold a Team Block: Click Here!

To Book Using your Team Block or to Book as an Individual, Not Within a Team Block: Click Here!

Our Hotel Policies

ALL TRAVELING TEAM MANAGERS/CLUB ADMINISTRATORS MUST REVIEW OUR HOTEL POLICY & RULES PRESENTED BELOW. FAILURE TO KNOW OUR POLICY COULD SUBJECT YOUR TEAM TO NON-ACCEPTANCE OR REMOVAL FROM THE TOURNAMENT

To insure the quality of service provided during a Firecracker Sports event this organization has put in a Hotel Obligation Policy.

WHAT IS A “HOTEL OBLIGATION” TOURNAMENT?

Simply stated, if your team requires hotel accommodations (team traveling 75 miles or more to the event’s destination city) your team MUST stay at one of the approved participating tournament partner hotels. Teams are required to designate a representative to block off rooms for the entire team through our hotel portal or staff. These participating hotels are reviewable by clicking on the “hotel” links on the tournament event page or on the main page of Firecracker Sports. We do not have a full “stay to play” policy but we do require 75% of your team/coaches to stay with one of our partner hotels through our website. Reservations should be made ASAP as the hotels fill up quick.

We do have a waiver policy, if teams do NOT want to book through us, but want to participate in our events. Please see below.

WHY DOES THIS TOURNAMENT HAVE A “HOTEL OBLIGATION?”

We work very hard to keep our costs down and also balance the economic impact a tournament like ours can have towards the communities and programs we serve. The approved hotels on this website assist with both concerns and the following are the benefits to our programs:

  • We only require 75% of your team to stay with one of our hotels. This allows 25% of your team to use points for their stay at another hotel, use Air BNB or camp sites for their travel preferences.
  • To make it manageable we have room nights “blocked” for our out-of-town teams from our participating tournament partner hotels.
  • Because we do not use a Third Party Housing Group and we negotiate large blocks of rooms with the hotels directly, you’ll find a wide variety of best pricing and amenities from our hotels.
  • Care in selecting location and proximity to our playing field. See our “fields/facilities” tab to find out where your team most likely will be playing so you can lock down hotels near your fields.
  • All of our tournament hotels have been approved due to their quality and desire to give our guests the best event experience.
  • Room inventory which is shared with the tournament to ensure that room demands can be met from year to year.

SCHEDULES & STANDINGS

Standings

 TeamWLTRARSRDW%
DateTimeGameDivisionLocationTeamScoreTeamNotes

Brackets

TOP PERFORMERS

RULES

There is no rules.

WEATHER

Tuesday, July 1st 6:00AM

All fields are open for pool play at this time. 

We will notify anyone of changes as weather hits here along with email communications through the event system. Please make sure you are connected to the roster, whether it's a coach, player or parent to receive email or SMS notifications. 

Merchandise tent will be setup at Cranston Stadium in Cranston, RI today if anyone is looking for last minute apparel or tournament souvenirs. 

`);printWindow.document.close(); }); /* function gpe_get_schedules_list() { const event_id = '39404'; let division_filter_drop = $("#division_filter_drop").val(); let pool_filter_drop = $("#pool_filter_drop").val(); let date_filter_drop = $("#date_filter_drop").val();// Show processing indicator (manually trigger it) table.destroy(); // Destroy old DataTable before making new AJAX call $("#schedules_table tbody").html('Loading...'); // Temporary loader row$.ajax({ url: gpe.ajax_url, type: 'post', data: { action: 'gpe_ajax', type: 'gpe_get_schedules_list', event_id: event_id, division_filter_drop: division_filter_drop, pool_filter_drop: pool_filter_drop, date_filter_drop: date_filter_drop }, success: function (response) { // Clear the table body and add new content $("#schedules_table tbody").empty().html(response.data.html);// Reinitialize DataTable with processing enabled table = $("#schedules_table").DataTable({ processing: true, serverSide: false, // Keep it false unless loading data dynamically searching: true, paging: true, autoWidth: false }); } }); } function gpe_get_standing_list() { const event_id = '39404'; let division_filter_drop = $("#division_filter_drop_for_standing").val(); let pool_filter_drop = $("#pool_filter_drop_for_standing").val(); // Show processing indicator (manually trigger it) standings_table.destroy(); // Destroy old DataTable before making new AJAX call $("#standings_table tbody").html('Loading...'); // Temporary loader row $.ajax({ url: gpe.ajax_url, type: 'post', data: { action: 'gpe_ajax', type: 'gpe_get_standing_list', event_id: event_id, division_filter_drop: division_filter_drop, pool_filter_drop: pool_filter_drop }, success: function (response) { // Clear the table body and add new content $("#standings_table tbody").empty().html(response.data.html); // Reinitialize DataTable with processing enabled standings_table = $("#standings_table").DataTable({ processing: true, serverSide: false, // Keep it false unless loading data dynamically searching: true, paging: false }); } }); }*/ $('#shareDropdown').change(function(){ var selectedOption = $(this).val(); if (selectedOption === "copy") { var pageLink = window.location.href; var tempInput = $(""); $("body").append(tempInput); tempInput.val(pageLink).select(); document.execCommand("copy"); tempInput.remove(); Swal.fire({ title:"Success", text:"Copied to clipboard!", icon:'success' }); // Reset dropdown selection $(this).prop('selectedIndex', 0); } }); }); let isSchedulesTableInitialized = false;function gpeCustomeTabsnew(evt, cityName) { var i, tabcontent, tablinks; tabcontent = document.getElementsByClassName("gpe_tabcontent"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; } tablinks = document.getElementsByClassName("gpe_tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" gpe_active", ""); } document.getElementById(cityName).style.display = "block"; evt.currentTarget.className += " gpe_active";if (cityName === "schedules_and_standings") { // ✅ Prevent reinitializing the DataTable if (!isSchedulesTableInitialized) { // Only call once window.gpe_get_schedules_list(); window.gpe_get_standing_list(); isSchedulesTableInitialized = true; } } }function gpeCustomeTabs(evt, cityName) { var i, tabcontent, tablinks; tabcontent = document.getElementsByClassName("gpe_tabcontent"); for (i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; } tablinks = document.getElementsByClassName("gpe_tablinks"); for (i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" gpe_active", ""); } document.getElementById(cityName).style.display = "block"; evt.currentTarget.className += " gpe_active"; // Redraw connectors when Bracket tab is activated if (cityName === "brackets"){ //alert("Bracket"); loadSavedBracket(); } if (cityName === "brackets" && window.globalSerialMap) { // Wait until the browser has painted the visible DOM //requestAnimationFrame(() => { //setTimeout(() => { //waitForLayoutAndDraw(window.globalSerialMap, 20); waitForLayoutAndDrawStable(window.globalSerialMap, 20); //}, 100); //}); } }// Get the element with id="defaultOpen" and click on it document.getElementById("defaultOpen").click(); var acc = document.getElementsByClassName("gpe_accordion"); var i; for (i = 0; i < acc.length; i++) { acc[i].addEventListener("click", function() { this.classList.toggle("active"); var panel = this.nextElementSibling; if (panel.style.maxHeight) { panel.style.maxHeight = null; } else { panel.style.maxHeight = panel.scrollHeight + "px"; } }); }var latitude = document.getElementById("latitude").value; var longitude = document.getElementById("longitude").value;var iframe = document.createElement("iframe"); iframe.src = "https://www.google.com/maps/embed?pb=!1m28!1m12!1m3!1d19797.686238814687!2d-71.3765509!3d41.8633795!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!4m13!3e6!4m5!1s0x89e45a80fc2b13ab%3A0x8f62f2e279889968!2sCurrent%20Location!3m2!1d" + latitude + "!2d" + longitude + "!4m5!1s0x89e45a7a2b41f7f3%3A0x6cb46461e8d0c1b4!2s" + latitude + "%2C" + longitude + "!3m2!1d" + latitude + "!2d" + longitude + "!5e0!3m2!1sen!2sus!4v1644233201036!5m2!1sen!2sus"; iframe.width = "528"; iframe.height = "295"; iframe.style.border = "0"; iframe.allowfullscreen = true;var mapContainer = document.getElementById("mapContainer"); mapContainer.innerHTML = ""; mapContainer.appendChild(iframe); function getPoolsByDivision(divisionId) { return $.ajax({ url: gpe.ajax_url, method: "POST", dataType: "json", data: { action: "gpe_ajax", type: "get_pools_save", division_id: divisionId } }); }function loadSavedBracket() { // Get the division ID from the division select dropdown const divisionId = $("#division_select").val(); // Get the current event ID dynamically from PHP const eventId = '39404';// If division ID is not selected, exit early if (!divisionId) return; $.when( $.ajax({ url: gpe.ajax_url, type: "POST", dataType: "json", data: { action: "gpe_ajax", type: "get_saved_bracket", event_id: eventId, division_id: divisionId } }), getPoolsByDivision(divisionId) ).done(function (bracketRes, poolsRes) { // Extract bracket data from the first response (bracketRes) let bracketData = bracketRes[0]?.data?.bracket_data || [];// Remove duplicates based on 'name' or 'id' or 'mapper_id' (whichever is unique) bracketData = bracketData.filter((item, index, self) => index === self.findIndex(t => t.name === item.name) );// Prepare a map of pool ID to pool title const poolMap = {}; poolsRes[0]?.data?.forEach(pool => { poolMap[pool.id] = pool.title; });// Call renderSavedBracket function to render the bracket renderSavedBracket(bracketData, poolMap); }).fail(function () { $("#bracket").html("
Error loading bracket.
"); }); // Perform both AJAX requests in parallel /*$.when( $.ajax({ url: gpe.ajax_url, // URL for the AJAX request type: "POST", // HTTP method dataType: "json", // Expected response type data: { action: "gpe_ajax", // WordPress action hook for AJAX type: "get_saved_bracket", // Custom action to get saved bracket data event_id: eventId, // The current event ID division_id: divisionId // The selected division ID } }), getPoolsByDivision(divisionId) // Function to get pools for the selected division ).done(function (bracketRes, poolsRes) { // Extract bracket data from the first response (bracketRes) const bracketData = bracketRes[0]?.data?.bracket_data || []; // Prepare a map of pool ID to pool title const poolMap = {}; poolsRes[0]?.data?.forEach(pool => { poolMap[pool.id] = pool.title; });// Call renderSavedBracket function to render the bracket renderSavedBracket(bracketData, poolMap); }).fail(function () { // In case of failure, show an error message in the bracket container $("#bracket").html("
Error loading bracket.
"); });*/ } function renderSavedBracket(bracketData, poolMap) { let bracketWrapper = $(".bracket_wrapper"); if (bracketWrapper.length === 0) { bracketWrapper = $("
").addClass("bracket_wrapper"); // Append it to the main container if it's missing $("#bracket").replaceWith(bracketWrapper); } else { bracketWrapper.empty(); // Clear existing content if already present } let bracketDiv = $("
").attr("id", "bracket"); bracketWrapper.append(bracketDiv); if (!bracketData || bracketData.length === 0) { bracketDiv.append(`

Bracket not available, please select a different division from the drop down menu

`); return; } let groups = {}; let serialMap = {}; let matchCounter = 1; bracketData.forEach(match => { if (match.is_important === "1") { if (!groups[match.group_number]) groups[match.group_number] = {}; if (!groups[match.group_number][match.column_number]) groups[match.group_number][match.column_number] = []; groups[match.group_number][match.column_number].push(match); } }); const sortedGroups = Object.keys(groups).sort(); const mainGroup = sortedGroups[0]; let groupScrollWrapper = $("
").addClass("group-scroll-wrapper"); for (let group of sortedGroups) { let teamSeedMap = {}; let groupSerialCounter = 1; let groupDiv = $("
").addClass("group").attr("data-group", group); let columnsContainer = $("
").addClass("columns-container"); let groupMatchesArray = Object.values(groups[group]).flat(); let groupLabel = groupMatchesArray[0]?.group_label ?? `Group ${group}`; let labelDiv = $("
").addClass("group-label").text(groupLabel); groupDiv.append(labelDiv); let columns = Object.keys(groups[group]).sort((a, b) => a - b); const groupMatches = {}; const allMatches = Object.values(groups[group]).flat(); const matchCount = allMatches.length; const isFiveTeam = allMatches.length === 4 && columns.length === 3; const isSevenTeam = matchCount === 6 && columns.length === 4; const isTenTeam = matchCount === 9 && columns.length === 4; const isNineTeam = matchCount === 8 && columns.length === 4; const isSixTeam = matchCount === 5 && columns.length === 3; const isTwelveTeam = matchCount === 11 && columns.length === 4; // Adjust if using 12 matches if (isTwelveTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name)); const displayOrder = [ allMatches[0], allMatches[1], allMatches[2], allMatches[3], // Round 1 allMatches[4], allMatches[5], allMatches[6], allMatches[7], // Round 2 allMatches[8], allMatches[9], // Semifinals allMatches[10] // Final ];let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]); let roundDiv4 = $("
").addClass("round").attr("data-column", columns[3]);displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`; let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
").addClass("match-container").css("margin-bottom", "20px"); matchContainer.append(matchDiv);match.serial = serial; if (!groupMatches[match.column_number]) groupMatches[match.column_number] = []; groupMatches[match.column_number].push(match); serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i < 4) roundDiv1.append(matchContainer); // Round 1 else if (i < 8) roundDiv2.append(matchContainer); // Round 2 else if (i < 10) roundDiv3.append(matchContainer); // Semis else roundDiv4.append(matchContainer); // Final });columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv4);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; }if (isSixTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name));const displayOrder = [ allMatches[0], // Match 1 allMatches[1], // Match 2 allMatches[2], // Match 3 allMatches[3], // Match 4 allMatches[4] // Final ];let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]);displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`;let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
").addClass("match-container").css("margin-bottom", "20px"); matchContainer.append(matchDiv);match.serial = serial; if (!groupMatches[match.column_number]) groupMatches[match.column_number] = []; groupMatches[match.column_number].push(match);serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i < 2) roundDiv1.append(matchContainer); else if (i < 4) roundDiv2.append(matchContainer); else roundDiv3.append(matchContainer); });columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; } if (isNineTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name));const displayOrder = [ allMatches[0], // Game 1 - Round 1 allMatches[1], // Game 2 - Round 2 allMatches[2], // Game 3 allMatches[3], // Game 4 allMatches[4], // Game 5 allMatches[5], // Game 6 - Round 3 allMatches[6], // Game 7 allMatches[7] // Game 8 - Round 4 (Final) ];let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]); let roundDiv4 = $("
").addClass("round").attr("data-column", columns[3]);displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`;let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
").addClass("match-container").css("margin-bottom", "20px"); matchContainer.append(matchDiv);match.serial = serial; if (!groupMatches[match.column_number]) groupMatches[match.column_number] = []; groupMatches[match.column_number].push(match);serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i === 0) roundDiv1.append(matchContainer); else if (i >= 1 && i <= 4) roundDiv2.append(matchContainer); else if (i === 5 || i === 6) roundDiv3.append(matchContainer); else if (i === 7) roundDiv4.append(matchContainer); });columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv4);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; }if (isTenTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name)); console.log("10-team allMatches:", allMatches.map(m => ({ name: m.name, id: m.id, column: m.column_number })));// Custom display order: Round 2 is visually reordered // Custom display order: Round 2 is visually reordered const displayOrder = [ allMatches[0], allMatches[1], // Round 1: Match 36, 37 allMatches[2], allMatches[3], allMatches[4], allMatches[5], // Round 2: Matches 3, 4, 5, 6 (in order) allMatches[6], allMatches[7], // Round 3: Matches 42–43 allMatches[8] // Final: Match 44 ];let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]); let roundDiv4 = $("
").addClass("round").attr("data-column", columns[3]);displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`;let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
") .addClass("match-container") .css("margin-bottom", "20px"); // ← add spacing between matchesmatchContainer.append(matchDiv);match.serial = serial; if (!groupMatches[match.column_number]) groupMatches[match.column_number] = []; groupMatches[match.column_number].push(match);serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i < 2) roundDiv1.append(matchContainer); // Round 1 else if (i >= 2 && i <= 5) { // Force desired visual order for Round 2: Matches 38, 39, 40, 41 // Map i to visual index: 2→0, 3→1, 4→2, 5→3 const round2Order = [2, 3, 4, 5]; const targetIndex = round2Order.indexOf(i);if (targetIndex !== -1) { const container = roundDiv2.find(".match-container").eq(targetIndex); if (container.length) { container.before(matchContainer); } else { roundDiv2.append(matchContainer); } } }else if (i < 8) roundDiv3.append(matchContainer); // Round 3 else roundDiv4.append(matchContainer); // Final });columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv4);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; }if (isFiveTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name)); const displayOrder = [allMatches[0], allMatches[2], allMatches[1], allMatches[3]]; groupMatches[columns[0]] = [allMatches[0]]; groupMatches[columns[1]] = [allMatches[2], allMatches[1]]; groupMatches[columns[2]] = [allMatches[3]]; let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]); displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`; let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`); const matchContainer = $("
").addClass("match-container"); matchContainer.append(matchDiv);match.serial = serial; serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i === 0) roundDiv1.append(matchContainer); else if (i === 1 || i === 2) roundDiv2.append(matchContainer); else if (i === 3) roundDiv3.append(matchContainer); });columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; }if (isSevenTeam) { allMatches.sort((a, b) => parseInt(a.name) - parseInt(b.name));const displayOrder = [ allMatches[0], // Match 1 allMatches[1], // Match 2 allMatches[2], // Match 3 allMatches[3], // Match 4 allMatches[4], // Match 5 allMatches[5] // Match 6 ];// Use only the first 3 columns for layout let roundDiv1 = $("
").addClass("round").attr("data-column", columns[0]); let roundDiv2 = $("
").addClass("round").attr("data-column", columns[1]); let roundDiv3 = $("
").addClass("round").attr("data-column", columns[2]);displayOrder.forEach((match, i) => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`;let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
").addClass("match-container"); matchContainer.append(matchDiv);match.serial = serial; if (!groupMatches["1"]) groupMatches["1"] = []; groupMatches["1"].push(match); serialMap[`${group}-${serial}`] = { ...match, group, column: match.column_number, serial };if (i < 3) { roundDiv1.append(matchContainer); // Matches 0,1,2 } else if (i < 5) { roundDiv2.append(matchContainer); // Matches 3,4 } else { roundDiv3.append(matchContainer); // Match 5 }});columnsContainer.append(roundDiv1); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv2); columnsContainer.append($("
").addClass("connector-column")); columnsContainer.append(roundDiv3);groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); } positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20);}, 100);continue; } columns.forEach((column, columnIndex) => { groups[group][column].sort((a, b) => parseInt(a.name) - parseInt(b.name)); let roundDiv = $("
").addClass("round").attr("data-column", column); groupMatches[column] = [];groups[group][column].forEach(match => { const serial = groupSerialCounter++; const matchId = `match-serial-${group}-${serial}`;let matchDiv = $("
") .addClass("match") .attr("id", matchId) .attr("data-match-index", match.name);let score1 = match.team1_score ?? "-"; let score2 = match.team2_score ?? "-"; // Inside your rendering function (e.g. inside a loop for each match) if (match.team_id1 && !teamSeedMap[match.team_id1] && match.seed1 && match.seed1 !== "0") { teamSeedMap[match.team_id1] = match.seed1; } if (match.team_id2 && !teamSeedMap[match.team_id2] && match.seed2 && match.seed2 !== "0") { teamSeedMap[match.team_id2] = match.seed2; }// Use stored seed if available; fallback to current if it's valid (not "0") let seed1 = ""; if (match.team_id1) { const storedSeed1 = teamSeedMap[match.team_id1]; seed1 = storedSeed1 ? `(${storedSeed1}) ` : (match.seed1 && match.seed1 !== "0" ? `(${match.seed1}) ` : ""); }let seed2 = ""; if (match.team_id2) { const storedSeed2 = teamSeedMap[match.team_id2]; seed2 = storedSeed2 ? `(${storedSeed2}) ` : (match.seed2 && match.seed2 !== "0" ? `(${match.seed2}) ` : ""); }//let team1 = match.team_name1 ?? "Team 1"; //let team2 = match.team_name2 ?? "Team 2"; let team1 = match.source_label_team1 && match.source_label_team1.trim() !== "" ? match.source_label_team1 : (match.team_name1 ?? "Team 1"); let team2 = match.source_label_team2 && match.source_label_team2.trim() !== "" ? match.source_label_team2 : (match.team_name2 ?? "Team 2"); // Combine seed and name for display let displayTeam1 = `${seed1}${team1}`; let displayTeam2 = `${seed2}${team2}`;let startTime = formatStartDateTime(match.start_date, match.start_time); let location = match.location || "";matchDiv.html(`
${match.name}
${startTime}
${location}
${displayTeam1} ${score1}
${displayTeam2} ${score2}
`);const matchContainer = $("
").addClass("match-container"); matchContainer.append(matchDiv); roundDiv.append(matchContainer);match.serial = serial; serialMap[`${group}-${serial}`] = { ...match, group, column, serial };groupMatches[column].push(match); });columnsContainer.append(roundDiv); if (columnIndex < columns.length - 1) { columnsContainer.append($("
").addClass("connector-column")); } });groupDiv.append(columnsContainer); groupScrollWrapper.append(groupDiv);const groupNumbers = Object.keys(groups); // make sure 'groups' is defined and validconst importantMatchExists = Object.values(groupMatches) .flat() .some(m => m.is_importantlast === "1");setTimeout(() => { if (importantMatchExists) { const currentGroupNumber = group; const currentIndex = groupNumbers.indexOf(currentGroupNumber); const prevGroupNumber = currentIndex > 0 ? groupNumbers[currentIndex - 1] : null; const lastChecked = true;positionMatcheslast(groupMatches, currentGroupNumber, prevGroupNumber, lastChecked); }positionMatches(groupMatches, group); //waitForLayoutAndDraw(serialMap, 20); waitForLayoutAndDrawStable(serialMap, 20); }, 100);}bracketDiv.append(groupScrollWrapper);const observer = new MutationObserver(() => { const visible = $(".bracket_wrapper:visible").length > 0; if (visible) { setTimeout(() => { //waitForLayoutAndDraw(window._lastSerialMap || {}, 20); waitForLayoutAndDrawStable(window._lastSerialMap || {}, 20);}, 100); } });observer.observe(document.body, { childList: true, subtree: true }); window.globalSerialMap = serialMap; } function isElementVisible(el) { if (!el) return false; const style = window.getComputedStyle(el); const rect = el.getBoundingClientRect();return ( style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0' && rect.width > 0 && rect.height > 0 && document.body.contains(el) ); }function positionMatcheslast(groupMatches, group, prevGroupNumber, isLastCheckboxChecked = false) { if (!isLastCheckboxChecked) { // If checkbox not checked, hide and clear SVG just in case const existingSvg = document.getElementById('global-connector-svg'); if (existingSvg) { existingSvg.style.display = 'none'; while (existingSvg.firstChild) existingSvg.removeChild(existingSvg.firstChild); console.log("❌ Checkbox not checked — hiding SVG and clearing lines."); } return; }const prevGroup = document.querySelector(`.group[data-group="${prevGroupNumber}"]`); const currGroup = document.querySelector(`.group[data-group="${group}"]`);let svg = document.getElementById('global-connector-svg'); if (!svg) { svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.id = 'global-connector-svg'; svg.style.position = 'absolute'; svg.style.top = '0'; svg.style.left = '0'; svg.style.zIndex = '9999'; svg.style.pointerEvents = 'none'; document.body.appendChild(svg); console.log("📦 Created global SVG container with id='global-connector-svg'"); }// Always update size svg.style.width = document.documentElement.scrollWidth + 'px'; svg.style.height = document.documentElement.scrollHeight + 'px';if (isElementVisible(prevGroup) && isElementVisible(currGroup)) { const prevRect = prevGroup.getBoundingClientRect(); const currRect = currGroup.getBoundingClientRect();const prevPos = { x: prevRect.left + window.scrollX + prevRect.width, y: prevRect.top + window.scrollY + prevRect.height / 2 };const currPos = { x: currRect.left + window.scrollX, y: currRect.top + window.scrollY + currRect.height / 2 };// Clear old lines while (svg.firstChild) { svg.removeChild(svg.firstChild); }// Draw new line const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute("x1", prevPos.x); line.setAttribute("y1", prevPos.y); line.setAttribute("x2", currPos.x); line.setAttribute("y2", currPos.y); line.setAttribute("stroke", "#999"); line.setAttribute("stroke-width", "2");svg.appendChild(line);svg.style.display = 'block'; // show SVG console.log("✅ Line appended to global SVG and SVG shown"); } else { // No visible groups → clear and hide SVG while (svg.firstChild) { svg.removeChild(svg.firstChild); } svg.style.display = 'none'; console.warn("⚠️ One or both group elements are hidden or missing — hiding SVG"); } }function formatStartDateTime(start_date, start_time) { // Check for null, undefined, or empty values if (!start_date || !start_time || typeof start_time !== 'string' || !start_time.includes(' ')) { return ""; }const timePart = start_time.split(' ')[1]; if (!timePart) return "";const timeObj = new Date(`1970-01-01T${timePart}`); if (isNaN(timeObj.getTime())) { return ""; }// Convert start_date from "YYYY-MM-DD" to "MM/DD/YYYY" const [year, month, day] = start_date.split('-'); if (!year || !month || !day) return "";const formattedDate = `${month}/${day}/${year}`;const timeFormatted = timeObj.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true, // 24-hour format });return `${formattedDate} ${timeFormatted} EST`; }function positionMatches(groupMatches, group) { const levels = Object.keys(groupMatches).sort((a, b) => +a - +b); const matchCenters = {}; const spacing = 200; let bottomMost = 0; const totalMatches = Object.values(groupMatches).flat().length; const isFiveTeamLayout = totalMatches === 4; const isSixTeamLayout = totalMatches === 5; const isSevenTeamLayout = totalMatches === 6; const isTenTeamLayout = totalMatches === 9; const isNineTeamLayout = totalMatches === 8; const isTwelveTeamLayout = totalMatches === 11; const isThreeTeamLayout = totalMatches === 2;if (isThreeTeamLayout) { const serialPositions = { 1: 200, // Match 1: Team A vs Team B 2: 350, // Match 2: Loser of Match 1 vs Team C };[1, 2].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } });// Draw connector lines based on bracket logic drawConnectorLine(group, 1, 2); // Winner of Match 1 goes to FinaldrawChampionLine(group, 2);// Set container height $(`.group[data-group="${group}"]`).css("height", `${bottomMost + 200}px`); return; } if (isTwelveTeamLayout) { const serialPositions = { 1: 200, // Round 1 2: 400, 3: 600, 4: 800, 5: 100, 6: 300, // Quarterfinals 7: 500, 8: 700, 9: 200, 10: 600, // Semifinal 1 11: 400 // Final };// Position matches Object.keys(serialPositions).forEach((serialStr) => { const serial = parseInt(serialStr); const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } });// Draw connector lines (you may need to tweak based on actual match setup) drawConnectorLine(group, 1, 5, 'left', 'bottom'); drawConnectorLine(group, 2, 6, 'left', 'bottom'); drawConnectorLine(group, 3, 7, 'left', 'bottom'); drawConnectorLine(group, 4, 8, 'left', 'bottom'); drawConnectorLine(group, 5, 9, 'right', 'top'); drawConnectorLine(group, 6, 9, 'right', 'bottom'); drawConnectorLine(group, 7, 10, 'right', 'top'); drawConnectorLine(group, 8, 10, 'right', 'bottom'); drawConnectorLine(group, 9, 11, 'right', 'top'); drawConnectorLine(group, 10, 11, 'right', 'bottom'); drawChampionLine(group, 11);$(`.group[data-group="${group}"]`).css("height", `${bottomMost + 250}px`); return; }if (isNineTeamLayout) { const serialPositions = { 1: 200, // Game 1 (Round 1) 2: 100, // Game 2 (Round 2) 3: 300, // Game 3 4: 500, // Game 4 5: 700, // Game 5 6: 200, // Game 6 (Round 3) 7: 600, // Game 7 8: 400 // Game 8 (Final) };[1, 2, 3, 4, 5, 6, 7, 8].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } }); // Draw connector lines based on logical flow drawConnectorLine(group, 1, 2); // Game 1 → Game 2 drawConnectorLine(group, 2, 6); // Game 2 → Game 6 drawConnectorLine(group, 3, 6); // Game 3 → Game 6 drawConnectorLine(group, 4, 7); // Game 4 → Game 7 drawConnectorLine(group, 5, 7); // Game 5 → Game 7 drawConnectorLine(group, 6, 8); // Game 6 → Game 8 drawConnectorLine(group, 7, 8); // Game 7 → Game 8 drawChampionLine(group, 8);bottomMost = 0; [1, 2, 3, 4, 5, 6, 7, 8].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } }); drawChampionLine(group, 8); // Apply accurate height to container $(`.group[data-group="${group}"]`).css("height", `${bottomMost + 250}px`); return; }// Ten Team Layout if (isTenTeamLayout) { const serialPositions = { 1: 200, 2: 600, 3: 100, 4: 300, 5: 500, 6: 700, 7: 200, 8: 600, 9: 400 };// Loop through matches 1 to 9 to set positions [1, 2, 3, 4, 5, 6, 7, 8, 9].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } });// Draw connector lines for Round 1, 2, and 3 drawConnectorLine(group, 1, 3, 'left', 'bottom'); drawConnectorLine(group, 2, 5, 'left', 'bottom'); drawConnectorLine(group, 3, 7, 'right', 'top'); drawConnectorLine(group, 4, 7, 'right', 'bottom'); drawConnectorLine(group, 5, 8, 'right', 'top'); drawConnectorLine(group, 6, 8, 'right', 'bottom'); drawConnectorLine(group, 7, 9, 'right', 'top'); drawConnectorLine(group, 8, 9, 'right', 'bottom'); drawChampionLine(group, 9);bottomMost = 0; [1, 2, 3, 4, 5, 6, 7, 8, 9].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } }); drawChampionLine(group, 9); $(`.group[data-group="${group}"]`).css("height", `${bottomMost + 250}px`); return; } if (isSevenTeamLayout) { const matchCenters = {}; let bottomMost = 0;// More generous vertical spacing between 1 → 2 → 3 (adjusted for more matches) const serialPositions = { 1: 0, // Match 1 at the top 2: 180, // Match 2 below Match 1 3: 400, // Match 3 below Match 2 4: 620, // Match 4 below Match 3 (between 1 & 2) 5: 840, // Match 5 below Match 4 (between 2 & 3) 6: 1060, // Match 6 below Match 5 (between 4 & 5) 7: 1280 // Match 7 at the bottom (below Match 6) };// Position Matches 1–7 (Play-ins) [1, 2, 3, 4, 5, 6, 7].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } }); console.log('Height1'+bottomMost); // Adjust positions for the additional matches if necessary: // Match 4: between 1 & 2 (Positioned earlier in `serialPositions`) const $match4 = $(`#match-serial-${group}-4`).closest(".match-container"); if ($match4.length) { const centerY = (matchCenters[1] + matchCenters[2]) / 2 - $match4.outerHeight() / 2; $match4.css({ position: "absolute", top: `${centerY}px` }); matchCenters[4] = centerY + $match4.outerHeight() / 2; bottomMost = Math.max(bottomMost, centerY + $match4.outerHeight()); }// Match 5: between 2 and 3 — center with slight right offset const $match5 = $(`#match-serial-${group}-5`).closest(".match-container"); if ($match5.length && matchCenters[2] && matchCenters[3]) { const centerY = (matchCenters[2] + matchCenters[3]) / 2 - $match5.outerHeight() / 2; const rightOffset = 40; // slight offset to center horizontally $match5.css({ position: "absolute", top: `${centerY}px` }); matchCenters[5] = centerY + $match5.outerHeight() / 2; bottomMost = Math.max(bottomMost, centerY + $match5.outerHeight()); }// Match 6: between match 4 and 5, centered to the right const $match6 = $(`#match-serial-${group}-6`).closest(".match-container"); if ($match6.length && matchCenters[4] && matchCenters[5]) { const centerY = (matchCenters[4] + matchCenters[5]) / 2 - $match6.outerHeight() / 2; const rightOffset = 60; // adjust as needed for proper placement $match6.css({ position: "absolute", top: `${centerY}px` }); matchCenters[6] = centerY + $match6.outerHeight() / 2; bottomMost = Math.max(bottomMost, centerY + $match6.outerHeight()); }// Match 7: below match 6, at the bottom-most position const $match7 = $(`#match-serial-${group}-7`).closest(".match-container"); if ($match7.length && matchCenters[6]) { const centerY = matchCenters[6] + 180; // 180px down from match 6 $match7.css({ position: "absolute", top: `${centerY}px` }); matchCenters[7] = centerY + $match7.outerHeight() / 2; bottomMost = Math.max(bottomMost, centerY + $match7.outerHeight()); }// Draw connector lines between matches drawConnectorLine(group, 1, 4); drawConnectorLine(group, 2, 5); drawConnectorLine(group, 3, 5); drawConnectorLine(group, 4, 6); drawConnectorLine(group, 5, 6); drawConnectorLine(group, 6, 7); // Adding connector line for Match 7 drawChampionLine(group, 7);// Set container height based on the bottom-most position // Recalculate actual bottom-most point after all matches are positioned bottomMost = 0; [1, 2, 3, 4, 5, 6, 7].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const top = parseFloat($match.css("top")) || 0; const height = $match.outerHeight() || 0; bottomMost = Math.max(bottomMost, top + height); } }); // Apply accurate height to container drawChampionLine(group, 6); $(`.group[data-group="${group}"]`).css("height", `${bottomMost + 250}px`); return; } if (isSixTeamLayout) { const serialPositions = { 1: 225, // Game 1 2: 400, // Game 2 3: 100, // Game 3 (Winner G1 vs Team 5) 4: 525, // Game 4 (Winner G2 vs Team 6) 5: 300 // Game 5 (Final) };[1, 2, 3, 4, 5].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const y = serialPositions[serial]; $match.css({ position: "absolute", top: `${y}px` }); matchCenters[serial] = y + $match.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $match.outerHeight()); } });// Draw correct connector lines //drawConnectorLine(group, 1, 3, 'right', 'top'); //drawConnectorLine(group, 2, 4, 'right', 'bottom'); //drawConnectorLine(group, 3, 5, 'right', 'top'); //drawConnectorLine(group, 4, 5, 'right', 'bottom');// Adjust container height bottomMost = 0; [1, 2, 3, 4, 5].forEach((serial) => { const $match = $(`#match-serial-${group}-${serial}`).closest(".match-container"); if ($match.length) { const top = parseFloat($match.css("top")) || 0; const height = $match.outerHeight() || 0; bottomMost = Math.max(bottomMost, top + height); } }); drawChampionLine(group, 5); $(`.group[data-group="${group}"]`).css("height", `${bottomMost + 250}px`); return; }// Helper function function drawConnectorLine(group, fromSerial, toSerial) { const fromEl = $(`#match-serial-${group}-${fromSerial}`).closest(".match-container"); const toEl = $(`#match-serial-${group}-${toSerial}`).closest(".match-container");if (!fromEl.length || !toEl.length) { console.warn(`Cannot draw line: from ${fromSerial} or to ${toSerial} not found.`); return; }const $group = $(`.group[data-group="${group}"]`); const $svg = $group.find("svg.connector-layer");if (!$svg.length) { console.warn(`Missing SVG layer in group ${group}`); return; }const fromOffset = fromEl.position(); const toOffset = toEl.position();const fromX = fromEl.position().left + fromEl.outerWidth(); const fromY = fromOffset.top + fromEl.outerHeight() / 2; const toX = toEl.position().left; const toY = toOffset.top + toEl.outerHeight() / 2;const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute("x1", fromX); line.setAttribute("y1", fromY); line.setAttribute("x2", toX); line.setAttribute("y2", toY); line.setAttribute("stroke", "#333"); line.setAttribute("stroke-width", "2");$svg.append(line); }// Positioning for 5-team layout if (isFiveTeamLayout) { const serialPositions = { 1: 200, // First round (Play-in) 3: 80, // Semifinal 1 (top) 2: 320, // Semifinal 2 (bottom) 4: 200 // Final (centered between semi 1 and 2) };Object.entries(groupMatches).forEach(([level, matches]) => { matches.forEach((match) => { const matchId = `match-serial-${group}-${match.serial}`; const $el = $(`#${matchId}`).closest(".match-container");if (!$el.length) return;const y = serialPositions[match.serial] ?? 0; $el.css({ position: "absolute", top: `${y}px` });matchCenters[match.serial] = y + $el.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $el.outerHeight()); }); }); drawChampionLine(group, 4); $(`.group[data-group="${group}"]`).css("height", bottomMost + 160); return; }// Default layout for other brackets levels.forEach((level, levelIndex) => { const matches = groupMatches[level];matches.forEach((match, i) => { const matchId = `match-serial-${group}-${match.serial}`; const $el = $(`#${matchId}`).closest(".match-container");if (!$el.length) return;if (levelIndex === 0) { const pairIndex = Math.floor(i / 2); const y = i * spacing + pairIndex * 40; $el.css({ position: "absolute", top: `${y}px` }); matchCenters[match.serial] = y + $el.outerHeight() / 2; bottomMost = Math.max(bottomMost, y + $el.outerHeight()); } else { const prevLevel = levels[levelIndex - 1]; const prevMatches = groupMatches[prevLevel]; const prev1 = prevMatches[i * 2]; const prev2 = prevMatches[i * 2 + 1];if (prev1 && prev2) { const center1 = matchCenters[prev1.serial]; const center2 = matchCenters[prev2.serial]; const centerY = (center1 + center2) / 2; const topOffset = centerY - $el.outerHeight() / 2; $el.css({ position: "absolute", top: `${topOffset}px` }); matchCenters[match.serial] = centerY; bottomMost = Math.max(bottomMost, topOffset + $el.outerHeight()); } } }); });$(`.group[data-group="${group}"]`).css("height", bottomMost + 180); const lastLevel = levels[levels.length - 1]; const lastMatch = groupMatches[lastLevel]?.[0]; if (lastMatch) drawChampionLine(group, lastMatch.serial);} function drawChampionLine(group, finalMatchSerial) { console.log(`drawChampionLine called with group=${group}, finalMatchSerial=${finalMatchSerial}`);const finalMatch = $(`#match-serial-${group}-${finalMatchSerial}`).closest(".match-container"); if (!finalMatch.length) { console.warn("Final match element not found"); return; }const $group = $(`.group[data-group="${group}"]`); let $svg = $group.find("svg.connector-layer");if (!$svg.length) { console.warn(`Missing SVG layer in group ${group}, creating one.`); $svg = $(``); $group.append($svg); }const matchOffset = finalMatch.offset(); const svgOffset = $svg.offset();const matchRightX = matchOffset.left - svgOffset.left + finalMatch.outerWidth(); const matchCenterY = matchOffset.top - svgOffset.top + finalMatch.outerHeight() / 2;const $teams = finalMatch.find(".team-row"); let championName = ""; let highScore = -1;$teams.each(function () { const teamName = $(this).find(".team-name").text().trim(); const score = parseInt($(this).find(".score").text().trim(), 10); if (!isNaN(score) && score > 0 && score > highScore) { highScore = score; championName = teamName; } });if (!championName) { console.warn("Could not determine champion team"); return; }const lowerChampionName = championName.toLowerCase(); const skipKeywords = ["winner", "winner of game"]; const shouldSkip = skipKeywords.some(keyword => lowerChampionName.includes(keyword));if (shouldSkip) { console.log(`Skipping champion line: name "${championName}" includes skip keyword.`); return; }console.log(`Champion team: ${championName} with score ${highScore}`);const svgNS = "http://www.w3.org/2000/svg"; const lineLength = 180; const offsetY = 29; // how much to move everything down from matchCenterYconst lineY = matchCenterY + offsetY;// Draw line const line = document.createElementNS(svgNS, "line"); line.setAttribute("x1", matchRightX); line.setAttribute("y1", lineY); line.setAttribute("x2", matchRightX + lineLength); line.setAttribute("y2", lineY); line.setAttribute("stroke", "#ccc"); line.setAttribute("stroke-width", "2"); $svg.append(line);const centerX = matchRightX + lineLength / 2; // Champion name (above the line) const nameText = document.createElementNS(svgNS, "text"); nameText.setAttribute("x", centerX); nameText.setAttribute("y", lineY - 16); // slightly above the line nameText.setAttribute("text-anchor", "middle"); nameText.setAttribute("font-size", "15px"); nameText.setAttribute("font-weight", "bold"); nameText.setAttribute("fill", "#000"); nameText.setAttribute("style", "text-shadow: 1px 1px 2px #ccc;"); nameText.textContent = `${championName}`; $svg.append(nameText);// "Champion" label (below the line) const championText = document.createElementNS(svgNS, "text"); championText.setAttribute("x", centerX); championText.setAttribute("y", lineY + 26); // slightly below the line championText.setAttribute("text-anchor", "middle"); championText.setAttribute("font-size", "14px"); championText.setAttribute("font-style", "italic"); championText.setAttribute("fill", "#000"); championText.setAttribute("style", "text-shadow: 1px 1px 1px #eee;"); championText.textContent = "Champion"; $svg.append(championText);console.log("Champion line and labels drawn."); }function debounce(func, delay) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), delay); }; } /* function waitForLayoutAndDraw(serialMap, maxTries = 10) { if (window._isDrawingScheduled) return;window._isDrawingScheduled = true; window._lastSerialMap = serialMap;let tries = 0;function tryDraw() { const wrapper = document.querySelector(".bracket_wrapper");if (!wrapper) { if (tries < maxTries) { tries++; requestAnimationFrame(tryDraw); } else { window._isDrawingScheduled = false; // Reset if failed after all tries } return; }const rect = wrapper.getBoundingClientRect(); if (rect.width < 100 || rect.height < 100) { if (tries < maxTries) { tries++; requestAnimationFrame(tryDraw); } else { window._isDrawingScheduled = false; } return; }// ✅ Only draw once per layout session if (!wrapper.dataset.hasDrawn) { drawBracketConnectors(serialMap, true); wrapper.dataset.hasDrawn = "true"; // 🔽 🔽 Add this block here 🔽 🔽 document.querySelectorAll('.group').forEach(group => { const maxHeight = 900; if (group.offsetHeight > maxHeight && !group.classList.contains('group-scroll')) { group.classList.add('group-scroll'); console.log("✅ group-scroll added to:", group); } }); }// 🟢 Reset the flag after a short time (if needed later) setTimeout(() => { window._isDrawingScheduled = false; wrapper.dataset.hasDrawn = ""; // Allow future redraws if needed }, 1000);const scrollTarget = document.querySelector(".group-scroll-wrapper");if (!scrollTarget.dataset.scrollListenerAdded && window.innerWidth <= 768) { console.log("🟢 Mobile scroll listener added");scrollTarget.addEventListener( "scroll", debounce(() => { console.log("🔁 Scrolling (mobile)"); drawBracketConnectors(window._lastSerialMap, true); }, 2) );scrollTarget.dataset.scrollListenerAdded = "true"; }requestAnimationFrame(tryDraw); }*/ function waitForLayoutAndDraw(serialMap, maxTries = 10) { if (window._isDrawingScheduled) return;window._isDrawingScheduled = true; window._lastSerialMap = serialMap;let tries = 0;function tryDraw() { const wrapper = document.querySelector(".bracket_wrapper");if (!wrapper) { if (tries < maxTries) { tries++; requestAnimationFrame(tryDraw); } else { window._isDrawingScheduled = false; } return; }const rect = wrapper.getBoundingClientRect(); if (rect.width < 100 || rect.height < 100) { if (tries < maxTries) { tries++; requestAnimationFrame(tryDraw); } else { window._isDrawingScheduled = false; } return; }// ✅ Draw once per layout if (!wrapper.dataset.hasDrawn) { drawBracketConnectors(serialMap, true); wrapper.dataset.hasDrawn = "true";document.querySelectorAll(".group").forEach(group => { const maxHeight = 900; if (group.offsetHeight > maxHeight && !group.classList.contains("group-scroll")) { group.classList.add("group-scroll"); console.log("✅ group-scroll added to:", group); } }); }// 🟢 Reset draw flag after timeout setTimeout(() => { window._isDrawingScheduled = false; wrapper.dataset.hasDrawn = ""; }, 1000);// ✅ ADD SCROLL LISTENER FOR MOBILE ONLY console.log("Window width:", window.innerWidth); // Inside tryDraw() const scrollTarget = document.querySelector(".group-scroll-wrapper");if (!scrollTarget.dataset.scrollListenerAdded && window.innerWidth <= 768) { console.log("🟢 Mobile scroll listener added");scrollTarget.addEventListener( "scroll", debounce(() => { console.log("🔁 Scrolling (mobile)"); drawBracketConnectors(window._lastSerialMap, true); }, 2) );scrollTarget.dataset.scrollListenerAdded = "true"; }}requestAnimationFrame(tryDraw); }function waitForLayoutAndDrawStable(serialMap, maxTries = 10) { let tries = 0;function attempt() { const wrapper = document.querySelector(".bracket_wrapper"); const groupReady = Array.from(document.querySelectorAll(".group")).every(el => el.offsetHeight > 200);if (!groupReady && tries < maxTries) { tries++; return setTimeout(attempt, 100); // Wait for layout }waitForLayoutAndDraw(serialMap); // ⬅️ Only call once layout is stable }attempt(); } function drawBracketConnectors(serialMap, forceRedraw = false) { console.log("Ketan test"); const svgNS = "http://www.w3.org/2000/svg"; let svg = document.getElementById("bracket-connector-svg"); const wrapper = document.querySelector(".bracket_wrapper");const wrapperRect = wrapper.getBoundingClientRect(); const scrollLeft = wrapper.scrollLeft; const scrollTop = wrapper.scrollTop;if (!svg) { svg = document.createElementNS(svgNS, "svg"); svg.setAttribute("id", "bracket-connector-svg"); svg.style.position = "absolute"; svg.style.top = "0"; svg.style.left = "0"; svg.style.pointerEvents = "none"; wrapper.appendChild(svg); }svg.style.width = wrapper.scrollWidth + "px"; svg.style.height = wrapper.scrollHeight + "px";const layoutHash = JSON.stringify( Object.values(serialMap).map(m => { const el = document.getElementById(`match-serial-${m.group}-${m.serial}`); const rect = el?.getBoundingClientRect(); return { serial: m.serial, group: m.group, column: m.column, rect: rect ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height } : null }; }) );if (!forceRedraw && window._lastLayoutHash === layoutHash) return; window._lastLayoutHash = layoutHash;while (svg.firstChild) svg.removeChild(svg.firstChild);const groupMap = {}; for (const serial in serialMap) { const match = serialMap[serial]; if (!groupMap[match.group]) groupMap[match.group] = []; groupMap[match.group].push(match); }for (const group in groupMap) { const matches = groupMap[group].sort((a, b) => +a.serial - +b.serial); const matchMap = {};matches.forEach(match => { matchMap[match.serial] = match; match.el = document.getElementById(`match-serial-${group}-${match.serial}`); match.rect = match.el?.getBoundingClientRect(); });const isFiveMatch = matches.length === 4; const isThreeTeam = matches.length === 2 && matchMap["1"] && matchMap["2"]; if ( matches.length === 11 && matchMap["1"] && matchMap["2"] && matchMap["3"] && matchMap["4"] && matchMap["5"] && matchMap["6"] && matchMap["7"] && matchMap["8"] && matchMap["9"] && matchMap["10"] && matchMap["11"] ) { const m = matchMap;// Round 1 → Round 2 drawLConnectorCustom(m[1].rect, m[5].rect, "bottom", 40); drawLConnectorCustom(m[2].rect, m[6].rect, "bottom", 40);drawLConnectorCustom(m[3].rect, m[7].rect, "bottom", 40); drawLConnectorCustom(m[4].rect, m[8].rect, "bottom", 40);// Seeds 1-4 with byes go directly to M7–M10 // Assume M5 feeds M7, M6 feeds M8, and so ondrawLConnectorCustom(m[5].rect, m[9].rect, "top", 40); drawLConnectorCustom(m[6].rect, m[9].rect, "bottom", 40);drawLConnectorCustom(m[7].rect, m[10].rect, "top", 40); // adjust if seed logic differs drawLConnectorCustom(m[8].rect, m[10].rect, "bottom", 40);// Round 2 → Semifinal drawLConnectorCustom(m[9].rect, m[11].rect, "top", 40); drawLConnectorCustom(m[10].rect, m[11].rect, "bottom", 40);continue; }if ( matches.length === 9 && matchMap["1"] && matchMap["2"] && matchMap["3"] && matchMap["4"] && matchMap["5"] && matchMap["6"] && matchMap["7"] && matchMap["8"] && matchMap["9"] ) { const m1 = matchMap[1]; const m2 = matchMap[2]; const m3 = matchMap[3]; const m4 = matchMap[4]; const m5 = matchMap[5]; const m6 = matchMap[6]; const m7 = matchMap[7]; const m8 = matchMap[8]; const m9 = matchMap[9];// Round 1 if (m1?.rect && m5?.rect) drawLConnectorCustom(m1.rect, m3.rect, "bottom", 40); if (m2?.rect && m6?.rect) drawLConnectorCustom(m2.rect, m5.rect, "bottom", 40);// Round 2 if (m3?.rect && m7?.rect) drawLConnectorCustom(m3.rect, m7.rect, "top", 40); if (m4?.rect && m7?.rect) drawLConnectorCustom(m4.rect, m7.rect, "bottom", 40); if (m5?.rect && m8?.rect) drawLConnectorCustom(m5.rect, m8.rect, "top", 40); if (m6?.rect && m8?.rect) drawLConnectorCustom(m6.rect, m8.rect, "bottom", 40);// Round 3 (Final) if (m7?.rect && m9?.rect) drawLConnectorCustom(m7.rect, m9.rect, "top", 40); if (m8?.rect && m9?.rect) drawLConnectorCustom(m8.rect, m9.rect, "bottom", 40);continue; } if (matches.length === 5) { // Adjusted for 5 matches console.log("matches.length === 5", matches); // log matches array console.log("matchMap:", matchMap); // log matchMap objectconst m1 = matchMap[1]; const m2 = matchMap[2]; const m3 = matchMap[3]; const m4 = matchMap[4]; const m5 = matchMap[5]; // Remove m6 because it's not available// Round 1 → Round 2 // M1 → M4 (top) if (m1?.rect && m3?.rect) { console.log("Drawing connector for M1 to M3"); drawLConnectorCustom(m1.rect, m3.rect, "top", 40); }// M2 → M5 (top) if (m2?.rect && m4?.rect) { console.log("Drawing connector for M2 to M4"); drawLConnectorCustom(m2.rect, m4.rect, "top", 40); }// M3 → M5 (bottom) if (m3?.rect && m5?.rect) { console.log("Drawing connector for M3 to M5"); drawLConnectorCustom(m3.rect, m5.rect, "bottom", 40); }// Round 2 → Final // M4 → (no M6, adjust logic based on match availability) if (m4?.rect && m5?.rect) { console.log("Drawing connector for M4 to the final match"); // Draw a connector to the final match (if you have a final setup) // You might have a fallback if there's no M6 drawLConnectorCustom(m4.rect,m5.rect, "top", 40); }continue; // use continue, not return — don't break whole function }if (matches.length === 6 && matchMap["1"] && matchMap["2"] && matchMap["3"] && matchMap["4"] && matchMap["5"] && matchMap["6"]) {const m1 = matchMap[1]; const m2 = matchMap[2]; const m3 = matchMap[3]; const m4 = matchMap[4]; const m5 = matchMap[5]; const m6 = matchMap[6];// Round 1 → Round 2 // M1 → M4 (top) if (m1?.rect && m4?.rect) drawLConnectorCustom(m1.rect, m4.rect, "top", 40);// M2 → M5 (top) if (m2?.rect && m5?.rect) drawLConnectorCustom(m2.rect, m5.rect, "top", 40);// M3 → M5 (bottom) if (m3?.rect && m5?.rect) drawLConnectorCustom(m3.rect, m5.rect, "bottom", 40);// Round 2 → Final // M4 → M6 (top) if (m4?.rect && m6?.rect) drawLConnectorCustom(m4.rect, m6.rect, "top", 40);// M5 → M6 (bottom) if (m5?.rect && m6?.rect) drawLConnectorCustom(m5.rect, m6.rect, "bottom", 40);continue; }if (isThreeTeam) { console.log("This is a testing"); const fromMatch = matchMap[1]; const toMatch = matchMap[2];if (fromMatch?.rect && toMatch?.rect) { drawLConnectorCustom(fromMatch.rect, toMatch.rect, "right", 40); }continue; // only skip current group } /*if (matches.length === 5) { const m1 = matchMap[1], m2 = matchMap[2]; const m3 = matchMap[3], m4 = matchMap[4], m5 = matchMap[5];if (m1?.rect && m4?.rect) drawLConnectorCustom(m1.rect, m4.rect, "top", 40); if (m2?.rect && m4?.rect) drawLConnectorCustom(m2.rect, m4.rect, "bottom", 40); if (m3?.rect && m5?.rect) drawLConnectorCustom(m3.rect, m5.rect, "top", 40); if (m4?.rect && m5?.rect) drawLConnectorCustom(m4.rect, m5.rect, "bottom", 40); }*/if (isFiveMatch) { const s1 = matchMap[1], s2 = matchMap[2], s3 = matchMap[3], s4 = matchMap[4];if (s1?.rect && s3?.rect) drawLConnectorCustom(s1.rect, s2.rect, "top"); if (s2?.rect && s4?.rect) drawLConnectorCustom(s2.rect, s4.rect, "top", 40); if (s3?.rect && s4?.rect) drawLConnectorCustom(s3.rect, s4.rect, "bottom", 80);continue; // use continue, not return — don't break whole function }const columns = [...new Set(matches.map(m => +m.column))].sort((a, b) => a - b);for (let colIndex = 1; colIndex < columns.length; colIndex++) { const currentCol = columns[colIndex]; const prevCol = columns[colIndex - 1];const currentMatches = matches.filter(m => +m.column === currentCol); const prevMatches = matches.filter(m => +m.column === prevCol);for (let i = 0; i < currentMatches.length; i++) { const targetMatch = currentMatches[i]; const input1 = prevMatches[i * 2]; const input2 = prevMatches[i * 2 + 1];if (input1?.rect && input2?.rect && targetMatch?.rect) { drawLConnectorCustom(input1.rect, targetMatch.rect, "top", 40); drawLConnectorCustom(input2.rect, targetMatch.rect, "bottom", 40); } else if ((input1?.rect || input2?.rect) && targetMatch?.rect) { const fromRect = input1?.rect || input2?.rect; const direction = input1?.rect ? "top" : "bottom"; drawLConnectorCustom(fromRect, targetMatch.rect, direction, 40); } } }function drawLConnectorCustom(fromRect, toRect, toPoint = "center", elbowOffset = 40) { const startX = fromRect.right - wrapperRect.left + scrollLeft; const startY = fromRect.top + fromRect.height / 2 - wrapperRect.top + scrollTop;let toY; const headerOffset = 48;if (toPoint === "top") { toY = toRect.top + headerOffset + 10 - wrapperRect.top + scrollTop; } else if (toPoint === "bottom") { toY = toRect.bottom - 10 - wrapperRect.top + scrollTop; } else { toY = toRect.top + toRect.height / 2 - wrapperRect.top + scrollTop; }const endX = toRect.left - wrapperRect.left + scrollLeft + 5;const path = document.createElementNS(svgNS, "path"); path.setAttribute("stroke", "#999"); path.setAttribute("stroke-width", "1.5"); path.setAttribute("fill", "none"); path.setAttribute("stroke-linecap", "round");const d = ` M ${startX},${startY} H ${endX} V ${toY} `;path.setAttribute("d", d.trim().replace(/\s+/g, " ")); svg.appendChild(path); }function drawTConnector(fromRect, toRect) { const startX = fromRect.right - wrapperRect.left + scrollLeft; const startY = fromRect.top + fromRect.height / 2 - wrapperRect.top + scrollTop;const endX = toRect.left - wrapperRect.left + scrollLeft; const endY = toRect.top + toRect.height / 2 - wrapperRect.top + scrollTop; const midX = (startX + endX) / 2;const path1 = document.createElementNS(svgNS, "path"); path1.setAttribute("stroke", "#999"); path1.setAttribute("stroke-width", "1.5"); path1.setAttribute("fill", "none"); path1.setAttribute("stroke-linecap", "round"); path1.setAttribute("d", `M ${startX},${startY} H ${midX}`); svg.appendChild(path1);const path2 = document.createElementNS(svgNS, "path"); path2.setAttribute("stroke", "#999"); path2.setAttribute("stroke-width", "1.5"); path2.setAttribute("fill", "none"); path2.setAttribute("stroke-linecap", "round"); path2.setAttribute("d", `M ${endX},${endY} H ${midX}`); svg.appendChild(path2);const verticalLine = document.createElementNS(svgNS, "path"); verticalLine.setAttribute("stroke", "#999"); verticalLine.setAttribute("stroke-width", "1.5"); verticalLine.setAttribute("fill", "none"); verticalLine.setAttribute("stroke-linecap", "round"); verticalLine.setAttribute("d", `M ${midX},${startY} V ${endY}`); svg.appendChild(verticalLine); }function drawHorizontalConnector(fromRect, toRect) { const startX = fromRect.right - wrapperRect.left + scrollLeft; const startY = fromRect.top + fromRect.height / 2 - wrapperRect.top + scrollTop;const endX = toRect.left - wrapperRect.left + scrollLeft;const path = document.createElementNS(svgNS, "path"); path.setAttribute("stroke", "#999"); path.setAttribute("stroke-width", "1.5"); path.setAttribute("fill", "none"); path.setAttribute("stroke-linecap", "round");const d = `M ${startX},${startY} H ${endX}`; path.setAttribute("d", d.trim().replace(/\s+/g, " ")); svg.appendChild(path); }function drawRightThenVertical(fromRect, toRect, align = "top") { const startX = fromRect.right - wrapperRect.left + scrollLeft; const startY = fromRect.top + fromRect.height / 2 - wrapperRect.top + scrollTop; const elbowX = startX + 40;const toY = align === "top" ? toRect.top - wrapperRect.top + scrollTop : toRect.bottom - wrapperRect.top + scrollTop;const toX = toRect.left - wrapperRect.left + scrollLeft;const path = document.createElementNS(svgNS, "path"); path.setAttribute("stroke", "#888"); path.setAttribute("stroke-width", "4"); path.setAttribute("fill", "none"); path.setAttribute("stroke-linejoin", "round"); path.setAttribute("stroke-linecap", "round");const d = ` M ${startX},${startY} H ${elbowX} V ${toY} H ${toX} `; path.setAttribute("d", d.trim().replace(/\s+/g, " ")); svg.appendChild(path); }function drawLine(from, to) { if (!from?.rect || !to?.rect) return;const x1 = from.rect.right + scrollLeft - wrapperRect.left; const y1 = from.rect.top + from.rect.height / 2 + scrollTop - wrapperRect.top;const x2 = to.rect.left + scrollLeft - wrapperRect.left; const y2 = to.rect.top + to.rect.height / 2 + scrollTop - wrapperRect.top;const midX = (x1 + x2) / 2;const path = document.createElementNS(svgNS, "path"); path.setAttribute("stroke", "#999"); path.setAttribute("stroke-width", "2"); path.setAttribute("fill", "none"); path.setAttribute("d", `M ${x1},${y1} H ${midX} V ${y2} H ${x2}`); svg.appendChild(path); }function drawDefaultConnect(input1, input2, targetMatch) { if (!input1?.rect || !input2?.rect || !targetMatch?.rect) return;const x1 = input1.rect.right + scrollLeft - wrapperRect.left; const y1 = input1.rect.top + input1.rect.height / 2 + scrollTop - wrapperRect.top;const x2 = input2.rect.right + scrollLeft - wrapperRect.left; const y2 = input2.rect.top + input2.rect.height / 2 + scrollTop - wrapperRect.top;const toX = targetMatch.rect.left + scrollLeft - wrapperRect.left; const toY = targetMatch.rect.top + targetMatch.rect.height / 2 + scrollTop - wrapperRect.top;const midX = (x1 + toX) / 2; const midY = (y1 + y2) / 2;const path = document.createElementNS(svgNS, "path"); path.setAttribute("stroke", "#999"); path.setAttribute("stroke-width", "2"); path.setAttribute("fill", "none");const d = ` M ${x1},${y1} H ${midX} M ${x2},${y2} H ${midX} M ${midX},${y1} V ${y2} M ${midX},${midY} H ${toX} `; path.setAttribute("d", d.trim().replace(/\s+/g, ' ')); svg.appendChild(path); } } }
×

Request for More Info