Files

333 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Seasons Filter Utility
// Parses and manages season/week data from the seasons constant file
class SeasonsFilter {
constructor() {
this.seasons = [];
this.loaded = false;
}
// Parse seasons text content into structured data
parseSeasons(content) {
const seasons = [];
const lines = content.split('\n');
let currentSeason = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;
// Check if it's a season header (e.g., "2025-V")
const seasonMatch = line.match(/^(\d{4}-[IVX]+)$/);
if (seasonMatch) {
if (currentSeason) {
seasons.push(currentSeason);
}
currentSeason = {
name: seasonMatch[1],
weeks: []
};
continue;
}
// Check if it's a week line
// Format: "week 1 (01.09 07.09) <t:1756735246:R>: max BR 14.0"
// or: "until eos (27.10 31.10) <t:1761487200:R>: max BR 5.0"
const weekMatch = line.match(/(?:week\s+(\d+)|until eos)\s+\((\d{2})\.(\d{2})\s*[–—]\s*(\d{2})\.(\d{2})\)\s*<t:(\d+):R>/);
if (weekMatch && currentSeason) {
const weekNum = weekMatch[1] ? parseInt(weekMatch[1]) : null;
const startDay = parseInt(weekMatch[2]);
const startMonth = parseInt(weekMatch[3]);
const endDay = parseInt(weekMatch[4]);
const endMonth = parseInt(weekMatch[5]);
const timestamp = parseInt(weekMatch[6]);
// Extract max BR if present (preserve .0 formatting)
const brMatch = line.match(/max BR ([\d.]+)/);
const maxBR = brMatch ? brMatch[1] : null; // Keep as string to preserve ".0"
// Use the Unix timestamp directly from the file (it's already correct!)
// The timestamp is the START of the week
const startDate = new Date(timestamp * 1000); // Convert to milliseconds
// Calculate end date: 7 days from start (or until end of season for "until eos")
// For "until eos" entries, we'll need to calculate based on the actual end day
let endDate;
// Determine year from season name (e.g., "2025-V" -> 2025)
const yearMatch = currentSeason.name.match(/^(\d{4})/);
const year = yearMatch ? parseInt(yearMatch[1]) : new Date().getFullYear();
// Build end date from the parsed end day/month
// Need to handle year rollover (e.g., December -> January)
let endYear = year;
if (endMonth < startMonth) {
endYear = year + 1; // Crossed into next year
}
endDate = new Date(endYear, endMonth - 1, endDay, 23, 59, 59, 999);
// Build display name with max BR (preserve decimal formatting like 14.0)
let displayName;
if (weekNum) {
displayName = `Week ${weekNum} (${weekMatch[2]}.${weekMatch[3]} ${weekMatch[4]}.${weekMatch[5]})`;
if (maxBR) displayName += ` - BR ${maxBR}`;
} else {
displayName = `Until EOS (${weekMatch[2]}.${weekMatch[3]} ${weekMatch[4]}.${weekMatch[5]})`;
if (maxBR) displayName += ` - BR ${maxBR}`;
}
currentSeason.weeks.push({
weekNumber: weekNum,
name: weekNum ? `Week ${weekNum}` : 'Until EOS',
startDate: startDate,
endDate: endDate,
startTimestamp: timestamp, // Store original Unix timestamp (seconds)
maxBR: maxBR,
displayName: displayName
});
}
}
// Add the last season
if (currentSeason) {
seasons.push(currentSeason);
}
// Post-process: set each week's endDate to the next week's startDate - 1ms
// This uses the exact Unix timestamps instead of the approximate text-parsed dates
seasons.forEach(season => {
season.weeks.forEach((week, i) => {
if (i + 1 < season.weeks.length) {
week.endDate = new Date(season.weeks[i + 1].startDate.getTime() - 1);
}
// Last week of each season keeps its text-parsed endDate
});
});
return seasons;
}
// Load seasons data from the constant file
async loadSeasons() {
if (this.loaded) {
return this.seasons;
}
try {
const response = await fetch('/constants/seasons');
if (!response.ok) {
throw new Error(`Failed to load seasons: ${response.status}`);
}
const content = await response.text();
this.seasons = this.parseSeasons(content);
this.loaded = true;
console.log('[Seasons Filter] Loaded seasons:', this.seasons);
return this.seasons;
} catch (error) {
console.error('[Seasons Filter] Error loading seasons:', error);
this.seasons = [];
this.loaded = false;
return [];
}
}
// Get all seasons
getSeasons() {
return this.seasons;
}
// Get a specific season by name
getSeason(seasonName) {
return this.seasons.find(s => s.name === seasonName);
}
// Get current season (based on current date)
getCurrentSeason() {
const now = new Date();
for (const season of this.seasons) {
for (const week of season.weeks) {
if (now >= week.startDate && now <= week.endDate) {
return season;
}
}
}
// If no current season found, return the most recent one
return this.seasons.length > 0 ? this.seasons[this.seasons.length - 1] : null;
}
// Get current week (based on current date)
getCurrentWeek() {
const now = new Date();
for (const season of this.seasons) {
for (const week of season.weeks) {
if (now >= week.startDate && now <= week.endDate) {
return { season, week };
}
}
}
return null;
}
// Get date range for a specific season
getSeasonDateRange(seasonName) {
const season = this.getSeason(seasonName);
if (!season || season.weeks.length === 0) {
return null;
}
return {
startDate: season.weeks[0].startDate,
endDate: season.weeks[season.weeks.length - 1].endDate,
season: season
};
}
// Get date range for a specific week in a season
getWeekDateRange(seasonName, weekNumber) {
const season = this.getSeason(seasonName);
if (!season) {
return null;
}
const week = season.weeks.find(w => w.weekNumber === weekNumber);
if (!week) {
return null;
}
return {
startDate: week.startDate,
endDate: week.endDate,
week: week
};
}
// Get all unique BR values across all seasons (returns array of strings, preserving ".0")
getAllBRValues() {
const brSet = new Set();
this.seasons.forEach(season => {
season.weeks.forEach(week => {
if (week.maxBR) {
brSet.add(week.maxBR);
}
});
});
// Sort descending (convert to float for comparison, but keep as strings)
return Array.from(brSet).sort((a, b) => parseFloat(b) - parseFloat(a));
}
// Get all weeks that match a specific BR
getWeeksByBR(maxBR) {
const matchingWeeks = [];
this.seasons.forEach(season => {
season.weeks.forEach(week => {
if (week.maxBR === maxBR) {
matchingWeeks.push({
season: season,
week: week,
displayName: `${season.name} - ${week.name}`
});
}
});
});
return matchingWeeks;
}
// Get combined date range for all weeks with a specific BR (for filtering multiple weeks)
getDateRangeForBR(maxBR) {
const weeks = this.getWeeksByBR(maxBR);
if (weeks.length === 0) {
return null;
}
// Return array of date ranges (one for each week with this BR)
return weeks.map(({ season, week }) => ({
startDate: week.startDate,
endDate: week.endDate,
seasonName: season.name,
weekName: week.name,
displayName: `${season.name} - ${week.displayName}`
}));
}
// Populate a select element with season options
populateSeasonSelect(selectElement, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = 'all';
allOption.textContent = window.__t ? window.__t('leaderboard.allSeasons') : 'All Seasons';
selectElement.appendChild(allOption);
}
// Add seasons in reverse order (most recent first)
for (let i = this.seasons.length - 1; i >= 0; i--) {
const season = this.seasons[i];
const option = document.createElement('option');
option.value = season.name;
option.textContent = season.name;
selectElement.appendChild(option);
}
}
// Populate a select element with BR options
populateBRSelect(selectElement, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = '';
allOption.textContent = window.__t ? window.__t('leaderboard.allBR') : 'All BR';
selectElement.appendChild(allOption);
}
const brValues = this.getAllBRValues();
brValues.forEach(br => {
const option = document.createElement('option');
option.value = br;
option.textContent = `BR ${br}`;
selectElement.appendChild(option);
});
}
// Populate a select element with week options for a given season
populateWeekSelect(selectElement, seasonName, includeAllOption = true) {
selectElement.innerHTML = '';
if (includeAllOption) {
const allOption = document.createElement('option');
allOption.value = 'all';
allOption.textContent = window.__t ? window.__t('leaderboard.allWeeks') : 'All Weeks';
selectElement.appendChild(allOption);
}
const season = this.getSeason(seasonName);
if (!season) {
return;
}
season.weeks.forEach(week => {
const option = document.createElement('option');
option.value = week.weekNumber !== null ? week.weekNumber.toString() : 'final';
option.textContent = week.displayName;
selectElement.appendChild(option);
});
}
}
// Global instance
window.seasonsFilter = window.seasonsFilter || new SeasonsFilter();