← back to Malden House

schedule.js

132 lines

// schedule.js — builds the Games calendar page from LA28_VENUES
// Uses window.LA28_VENUES exposed by signup.js (or reads directly if loaded first).

(function () {
  // signup.js currently runs a bunch of side-effects when loaded in-browser.
  // We only need LA28_VENUES, which is defined at module scope. To be safe,
  // we hard-mirror the dataset here; keep in sync with signup.js.
  const VENUES = [
    { name: "SoFi Stadium · Inglewood",       short: "SoFi",         headline: "Opening Ceremony",        dates: "Jul 14, 2028" },
    { name: "Rose Bowl · Pasadena",           short: "Rose Bowl",    headline: "Soccer (Football)",       dates: "Jul 11 – Jul 29, 2028" },
    { name: "Long Beach Convention Center",   short: "Long Beach Conv.", headline: "Handball",            dates: "Jul 15 – Jul 30, 2028" },
    { name: "Long Beach · Alamitos Beach",    short: "Alamitos Beach", headline: "Beach Volleyball",      dates: "Jul 15 – Jul 26, 2028" },
    { name: "Santa Monica Pier",              short: "Santa Monica", headline: "Triathlon · Marathon Swim", dates: "Jul 15 – Jul 19, 2028" },
    { name: "Exposition Park Aquatic Center", short: "Expo Aquatic", headline: "Swimming · Diving",        dates: "Jul 15 – Jul 21, 2028" },
    { name: "UCLA · Pauley Pavilion",         short: "Pauley Pavilion", headline: "Gymnastics (Artistic)", dates: "Jul 15 – Jul 23, 2028" },
    { name: "Dodger Stadium",                 short: "Dodger Stadium", headline: "Baseball / Softball",    dates: "Jul 17 – Jul 28, 2028" },
    { name: "Dignity Health Sports Park",     short: "Dignity Health", headline: "Track Cycling · Tennis", dates: "Jul 17 – Jul 22, 2028" },
    { name: "Sepulveda Basin Valley Complex", short: "Valley Complex", headline: "3×3 Basketball · BMX · Skate · Pentathlon", dates: "Jul 18 – Jul 28, 2028" },
    { name: "LA Memorial Coliseum",           short: "LA Coliseum",  headline: "Track & Field",            dates: "Jul 21 – Jul 30, 2028" },
    { name: "Crypto.com Arena · DTLA",        short: "Crypto.com Arena", headline: "Gymnastics Finals",    dates: "Jul 21 – Jul 23, 2028" },
    { name: "Peacock Theater · LA Live",      short: "Peacock Theater", headline: "Weightlifting",          dates: "Jul 23 – Jul 29, 2028" },
    { name: "Intuit Dome · Inglewood",        short: "Intuit Dome",  headline: "Basketball",               dates: "Jul 24 – Jul 30, 2028" },
    { name: "Riviera Country Club",           short: "Riviera CC",   headline: "Golf",                     dates: "Jul 25 – Jul 30, 2028" },
    // Closing ceremony — back at SoFi
    { name: "SoFi Stadium · Inglewood",       short: "SoFi",         headline: "Closing Ceremony",         dates: "Jul 30, 2028" },
  ];

  const MONTHS = { Jan:0, Feb:1, Mar:2, Apr:3, May:4, Jun:5, Jul:6, Aug:7, Sep:8, Oct:9, Nov:10, Dec:11 };
  const MNAMES = Object.keys(MONTHS);
  const DOW = ["SUN","MON","TUE","WED","THU","FRI","SAT"];

  function expandDates(str) {
    const clean = String(str || "").replace(/[–—]/g, "-").trim();
    let m = clean.match(/^(\w{3})\s+(\d{1,2})\s*-\s*(\w{3})\s+(\d{1,2}),\s*(\d{4})$/);
    if (m) {
      const [, m1, d1, m2, d2, y] = m;
      const start = new Date(+y, MONTHS[m1], +d1);
      const end   = new Date(+y, MONTHS[m2], +d2);
      const out = [];
      for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
        out.push({ key: d.toISOString().slice(0,10), date: new Date(d) });
      }
      return out;
    }
    m = clean.match(/^(\w{3})\s+(\d{1,2}),\s*(\d{4})$/);
    if (m) {
      const d = new Date(+m[3], MONTHS[m[1]], +m[2]);
      return [{ key: d.toISOString().slice(0,10), date: d }];
    }
    return [];
  }

  // ------------ Calendar render ------------
  function renderCalendar(filterVenue = "") {
    const byDay = new Map();
    const dayKeys = [];
    const d0 = new Date(2028, 6, 11); // Jul 11 (soccer starts)
    const dN = new Date(2028, 6, 30); // Jul 30 (closing)
    for (let d = new Date(d0); d <= dN; d.setDate(d.getDate() + 1)) {
      const k = d.toISOString().slice(0, 10);
      byDay.set(k, []);
      dayKeys.push(k);
    }
    VENUES.forEach(v => {
      if (filterVenue && v.short !== filterVenue) return;
      expandDates(v.dates).forEach(ex => {
        if (byDay.has(ex.key)) byDay.get(ex.key).push(v);
      });
    });

    const cal = document.getElementById("sched-calendar");
    const months = ["Jul 2028"];
    cal.innerHTML = dayKeys.map(k => {
      const d = new Date(k);
      const events = byDay.get(k);
      const isOpen = events.some(e => /Opening/i.test(e.headline));
      const isClose = events.some(e => /Closing/i.test(e.headline));
      const headerTone = isOpen ? "open" : isClose ? "close" : "";
      return `
        <article class="day ${events.length ? "" : "empty"} ${headerTone}">
          <header>
            <span class="day-dow">${DOW[d.getDay()]}</span>
            <span class="day-num">${d.getDate()}</span>
            <span class="day-mon">JUL</span>
            <span class="day-count">${events.length} event${events.length === 1 ? "" : "s"}</span>
          </header>
          <ul>
            ${events.map(e => `
              <li class="ev ${isOpen && /Opening/i.test(e.headline) ? "ev-open" : ""} ${isClose && /Closing/i.test(e.headline) ? "ev-close" : ""}">
                <b>${e.headline}</b>
                <span>${e.short}</span>
              </li>`).join("")}
            ${events.length === 0 ? `<li class="ev-none">Rest day — no competition</li>` : ""}
          </ul>
        </article>`;
    }).join("");
  }

  // ------------ Venue filter chips ------------
  function renderChips(active = "") {
    const chips = document.getElementById("filter-chips");
    const all = [...new Set(VENUES.map(v => v.short))];
    chips.innerHTML = `
      <button class="vchip ${active === "" ? "on" : ""}" data-v="">All venues</button>
      ${all.map(v => `<button class="vchip ${active === v ? "on" : ""}" data-v="${v}">${v}</button>`).join("")}
    `;
    chips.querySelectorAll(".vchip").forEach(b => b.addEventListener("click", () => {
      const v = b.getAttribute("data-v");
      renderChips(v);
      renderCalendar(v);
    }));
  }

  // ------------ Alphabetical grid ------------
  function renderSportGrid() {
    const sorted = [...VENUES].sort((a, b) => a.headline.localeCompare(b.headline));
    document.getElementById("sport-grid").innerHTML = sorted.map(v => `
      <article class="sport-card">
        <div class="sport-head">${v.headline}</div>
        <div class="sport-venue">${v.name}</div>
        <div class="sport-dates">${v.dates}</div>
      </article>
    `).join("");
  }

  document.addEventListener("DOMContentLoaded", () => {
    renderChips();
    renderCalendar();
    renderSportGrid();
  });
})();