anycoder-66ccd631 / index.html
alperall's picture
Upload folder using huggingface_hub
003da9d verified
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Akıllı Futbol Analiz & Tahmin Platformu</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
/* Custom CSS for modern look */
:root {
--primary-color: #1e3a8a;
--secondary-color: #3b82f6;
--success-color: #10b981;
--warning-color: #f59e0b;
--danger-color: #ef4444;
}
body {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.header-gradient {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.live-badge {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
.card {
border: none;
border-radius: 15px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-2px);
}
.prediction-bar {
height: 30px;
border-radius: 15px;
margin-bottom: 10px;
position: relative;
overflow: hidden;
}
.prediction-fill {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
transition: width 1s ease-in-out;
}
.team-form-badge {
width: 30px;
height: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
margin: 0 2px;
font-weight: bold;
font-size: 0.8rem;
}
.form-win { background-color: var(--success-color); color: white; }
.form-draw { background-color: var(--warning-color); color: white; }
.form-loss { background-color: var(--danger-color); color: white; }
.match-row:hover {
background-color: #f8fafc;
cursor: pointer;
}
.loading-spinner {
display: none;
}
.stats-card {
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
}
</style>
</head>
<body>
<!-- Header -->
<nav class="navbar navbar-expand-lg header-gradient py-3">
<div class="container">
<a class="navbar-brand text-white fw-bold fs-4" href="#">
<i class="fas fa-futbol me-2"></i>Akıllı Futbol Analiz & Tahmin Platformu
</a>
<div class="d-flex align-items-center gap-3">
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-white text-decoration-none small bg-dark bg-opacity-25 px-3 py-1 rounded">
<i class="fas fa-code me-1"></i>Built with anycoder
</a>
<span class="badge bg-success live-badge px-3 py-2">
<i class="fas fa-circle me-1" style="font-size: 8px;"></i>7/24 Canlı Veri
</span>
</div>
</div>
</nav>
<div class="container my-4">
<!-- Control Panel -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-white border-bottom-0 pt-4">
<h5 class="card-title mb-0">
<i class="fas fa-filter text-primary me-2"></i>Filtreleme ve Kontrol
</h5>
</div>
<div class="card-body">
<div class="row align-items-end">
<div class="col-md-4 mb-3">
<label class="form-label">Lig Seçimi</label>
<select class="form-select" id="leagueFilter">
<option value="all">Tüm Ligler</option>
<!-- Options will be populated dynamically -->
</select>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Manuel Takım Analizi</label>
<div class="input-group">
<input type="text" class="form-control" id="manualHomeTeam" placeholder="Ev Sahibi Takım">
<input type="text" class="form-control" id="manualAwayTeam" placeholder="Deplasman Takım">
</div>
</div>
<div class="col-md-4 mb-3">
<button class="btn btn-primary w-100" onclick="analyzeManualTeams()">
<i class="fas fa-search me-2"></i>Manuel Analiz Yap
</button>
</div>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-outline-primary" onclick="refreshData()">
<i class="fas fa-sync-alt me-2"></i>Verileri Yenile
</button>
<span class="text-muted ms-3 small" id="lastUpdateTime">
<i class="fas fa-clock me-1"></i>Son Güncelleme: -
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Upcoming Matches -->
<div class="col-lg-5 mb-4">
<div class="card h-100">
<div class="card-header bg-white border-bottom-0 pt-4 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-calendar-alt text-primary me-2"></i>Önümüzdeki Maçlar
</h5>
<div class="loading-spinner text-primary" id="matchesLoading">
<i class="fas fa-spinner fa-spin"></i>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0" id="matchesTable">
<thead class="table-light">
<tr>
<th>Tarih & Saat</th>
<th>Lig</th>
<th>Ev Sahibi</th>
<th>Deplasman</th>
<th>İşlem</th>
</tr>
</thead>
<tbody id="matchesTableBody">
<!-- Matches will be populated here -->
</tbody>
</table>
</div>
<div id="noMatchesMessage" class="text-center py-5 text-muted" style="display: none;">
<i class="fas fa-inbox fa-3x mb-3 opacity-25"></i>
<p>Maç bulunamadı</p>
</div>
</div>
</div>
</div>
<!-- Analysis Dashboard -->
<div class="col-lg-7 mb-4">
<div class="card h-100" id="analysisCard" style="display: none;">
<div class="card-header bg-white border-bottom-0 pt-4">
<h5 class="card-title mb-0">
<i class="fas fa-chart-line text-primary me-2"></i>Analiz Panosu
</h5>
</div>
<div class="card-body">
<!-- Match Info -->
<div class="text-center mb-4 p-3 bg-light rounded">
<h3 class="mb-3">
<span id="analysisHomeTeam" class="fw-bold text-primary">-</span>
<span class="mx-3 text-muted">vs</span>
<span id="analysisAwayTeam" class="fw-bold text-danger">-</span>
</h3>
<p class="text-muted mb-0" id="analysisMatchInfo">-</p>
</div>
<!-- Predictions -->
<div class="mb-4">
<h6 class="fw-bold mb-3"><i class="fas fa-percentage me-2"></i>Tahmin Olasılıkları</h6>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span id="homeWinLabel">Ev Sahibi Kazanır</span>
<span class="fw-bold" id="homeWinPercent">0%</span>
</div>
<div class="prediction-bar bg-light">
<div class="prediction-fill bg-primary" id="homeWinBar" style="width: 0%"></div>
</div>
</div>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span>Beraberlik</span>
<span class="fw-bold" id="drawPercent">0%</span>
</div>
<div class="prediction-bar bg-light">
<div class="prediction-fill bg-warning" id="drawBar" style="width: 0%"></div>
</div>
</div>
<div class="mb-2">
<div class="d-flex justify-content-between mb-1">
<span id="awayWinLabel">Deplasman Kazanır</span>
<span class="fw-bold" id="awayWinPercent">0%</span>
</div>
<div class="prediction-bar bg-light">
<div class="prediction-fill bg-danger" id="awayWinBar" style="width: 0%"></div>
</div>
</div>
</div>
<!-- Charts -->
<div class="row mb-4">
<div class="col-md-6 mb-3">
<canvas id="homeTeamChart"></canvas>
</div>
<div class="col-md-6 mb-3">
<canvas id="awayTeamChart"></canvas>
</div>
</div>
<!-- Detailed Stats -->
<div class="row">
<div class="col-md-6 mb-3">
<div class="card stats-card h-100">
<div class="card-body">
<h6 class="card-title text-primary" id="homeStatsTitle">Ev Sahibi İstatistikleri</h6>
<ul class="list-unstyled mb-0 mt-3">
<li class="mb-2"><i class="fas fa-futbol me-2 text-muted"></i>Ort. Gol: <span id="homeAvgGoals" class="fw-bold">-</span></li>
<li class="mb-2"><i class="fas fa-shield-alt me-2 text-muted"></i>Ort. Yenilen Gol: <span id="homeAvgConceded" class="fw-bold">-</span></li>
<li class="mb-2"><i class="fas fa-chart-bar me-2 text-muted"></i>Form: <span id="homeForm" class="fw-bold">-</span></li>
<li><i class="fas fa-trophy me-2 text-muted"></i>Puan Ort.: <span id="homeAvgPoints" class="fw-bold">-</span></li>
</ul>
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<div class="card stats-card h-100">
<div class="card-body">
<h6 class="card-title text-danger" id="awayStatsTitle">Deplasman İstatistikleri</h6>
<ul class="list-unstyled mb-0 mt-3">
<li class="mb-2"><i class="fas fa-futbol me-2 text-muted"></i>Ort. Gol: <span id="awayAvgGoals" class="fw-bold">-</span></li>
<li class="mb-2"><i class="fas fa-shield-alt me-2 text-muted"></i>Ort. Yenilen Gol: <span id="awayAvgConceded" class="fw-bold">-</span></li>
<li class="mb-2"><i class="fas fa-chart-bar me-2 text-muted"></i>Form: <span id="awayForm" class="fw-bold">-</span></li>
<li><i class="fas fa-trophy me-2 text-muted"></i>Puan Ort.: <span id="awayAvgPoints" class="fw-bold">-</span></li>
</ul>
</div>
</div>
</div>
</div>
<!-- Head to Head -->
<div class="mt-3" id="h2hSection" style="display: none;">
<h6 class="fw-bold mb-3"><i class="fas fa-history me-2"></i>Önceki Karşılaşmalar</h6>
<div class="table-responsive">
<table class="table table-sm table-bordered" id="h2hTable">
<thead class="table-light">
<tr>
<th>Tarih</th>
<th>Ev Sahibi</th>
<th>Skor</th>
<th>Deplasman</th>
</tr>
</thead>
<tbody id="h2hTableBody"></tbody>
</table>
</div>
</div>
<button class="btn btn-primary w-100 mt-3" onclick="refreshCurrentAnalysis()">
<i class="fas fa-sync-alt me-2"></i>Analizi Yenile
</button>
</div>
</div>
<!-- Empty State -->
<div class="card h-100" id="emptyAnalysisCard">
<div class="card-body d-flex flex-column justify-content-center align-items-center text-center p-5">
<i class="fas fa-chart-pie fa-4x text-muted mb-3 opacity-25"></i>
<h5 class="text-muted">Analiz için bir maç seçin</h5>
<p class="text-muted small">Sol taraftan bir maç seçerek veya yukarıdan manuel takım girerek analiz başlatabilirsiniz.</p>
</div>
</div>
</div>
</div>
<!-- Live Data Indicator -->
<div class="row">
<div class="col-12">
<div class="alert alert-info d-flex align-items-center mb-4" role="alert">
<i class="fas fa-info-circle me-2"></i>
<div>
<strong>Canlı Veri:</strong> Veriler her 60 saniyede bir otomatik olarak güncellenmektedir.
<span class="ms-2 badge bg-primary" id="nextUpdateCountdown">Sonraki güncelleme: 60s</span>
</div>
</div>
</div>
</div>
<!-- Disclaimer -->
<div class="row">
<div class="col-12">
<div class="alert alert-warning" role="alert">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>Uyarı:</strong> Bu platform Football-Data.org'dan alınan gerçek verilerle istatistiksel simülasyon yapar.
Kesin bir tahmin aracı veya bahis önerisi <strong>DEĞİLDİR</strong>. Sonuçlar garanti etmez.
Sorumlu oyun oynayınız.
</div>
</div>
</div>
</div>
<script>
// Configuration
const API_TOKEN = '326546a82e2848f1b66d2e859a8f0832';
const API_BASE_URL = 'https://api.football-data.org/v4';
let currentMatches = [];
let selectedMatch = null;
let homeTeamChart = null;
let awayTeamChart = null;
let autoRefreshInterval = null;
let countdownInterval = null;
let countdownValue = 60;
// DOM Elements
const matchesTableBody = document.getElementById('matchesTableBody');
const leagueFilter = document.getElementById('leagueFilter');
const lastUpdateTime = document.getElementById('lastUpdateTime');
const matchesLoading = document.getElementById('matchesLoading');
const noMatchesMessage = document.getElementById('noMatchesMessage');
// Initialize
document.addEventListener('DOMContentLoaded', () => {
initializeApp();
});
async function initializeApp() {
try {
await fetchUpcomingMatches();
startAutoRefresh();
updateLastUpdateTime();
} catch (error) {
console.error('Initialization error:', error);
showError('Uygulama başlatılırken bir hata oluştu. Lütfen sayfayı yenileyin.');
}
}
// Fetch upcoming matches for next 7 days
async function fetchUpcomingMatches() {
showLoading(true);
try {
// Calculate date range (today to +7 days)
const today = new Date();
const nextWeek = new Date(today);
nextWeek.setDate(today.getDate() + 7);
const dateFrom = formatDateForAPI(today);
const dateTo = formatDateForAPI(nextWeek);
// Fetch matches from API
const response = await fetch(`${API_BASE_URL}/matches?dateFrom=${dateFrom}&dateTo=${dateTo}`, {
headers: {
'X-Auth-Token': API_TOKEN
}
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
currentMatches = data.matches || [];
// Populate league filter
populateLeagueFilter(currentMatches);
// Display matches
displayMatches(currentMatches);
updateLastUpdateTime();
} catch (error) {
console.error('Error fetching matches:', error);
showError('Maç verileri çekilirken bir hata oluştu. API limiti aşılmış olabilir veya bağlantı hatası vardır.');
} finally {
showLoading(false);
}
}
// Format date for API (YYYY-MM-DD)
function formatDateForAPI(date) {
return date.toISOString().split('T')[0];
}
// Format date for display
function formatDateForDisplay(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('tr-TR', {
day: '2-digit',
month '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// Populate league filter dropdown
function populateLeagueFilter(matches) {
const competitions = [...new Set(matches.map(m => m.competition.name))].sort();
const currentValue = leagueFilter.value;
leagueFilter.innerHTML = '<option value="all">Tüm Ligler</option>';
competitions.forEach(comp => {
const option = document.createElement('option');
option.value = comp;
option.textContent = comp;
leagueFilter.appendChild(option);
});
leagueFilter.value = currentValue;
}
// Display matches in table
function displayMatches(matches) {
matchesTableBody.innerHTML = '';
if (matches.length === 0) {
noMatchesMessage.style.display = 'block';
return;
}
noMatchesMessage.style.display = 'none';
matches.forEach(match => {
const row = document.createElement('tr');
row.className = 'match-row';
const homeTeam = match.homeTeam.name;
const awayTeam = match.awayTeam.name;
const competition = match.competition.name;
const matchDate = formatDateForDisplay(match.utcDate);
row.innerHTML = `
<td class="align-middle">${matchDate}</td>
<td class="align-middle"><span class="badge bg-secondary">${competition}</span></td>
<td class="align-middle fw-bold">${homeTeam}</td>
<td class="align-middle fw-bold">${awayTeam}</td>
<td class="align-middle">
<button class="btn btn-sm btn-primary" onclick="analyzeMatch('${match.id}')">
<i class="fas fa-chart-bar me-1"></i>Analiz Et
</button>
</td>
`;
matchesTableBody.appendChild(row);
});
}
// Filter matches by league
leagueFilter.addEventListener('change', (e) => {
const selectedLeague = e.target.value;
if (selectedLeague === 'all') {
displayMatches(currentMatches);
} else {
const filtered = currentMatches.filter(m => m.competition.name === selectedLeague);
displayMatches(filtered);
}
});
// Analyze specific match
async function analyzeMatch(matchId) {
const match = currentMatches.find(m => m.id == matchId);
if (!match) return;
selectedMatch = match;
document.getElementById('analysisHomeTeam').textContent = match.homeTeam.name;
document.getElementById('analysisAwayTeam').textContent = match.awayTeam.name;
document.getElementById('analysisMatchInfo').textContent = `${match.competition.name} - ${formatDateForDisplay(match.utcDate)}`;
await performAnalysis(match.homeTeam.id, match.awayTeam.id, match.homeTeam.name, match.awayTeam.name);
}
// Manual team analysis
async function analyzeManualTeams() {
const homeTeamName = document.getElementById('manualHomeTeam').value.trim();
const awayTeamName = document.getElementById('manualAwayTeam').value.trim();
if (!homeTeamName || !awayTeamName) {
alert('Lütfen her iki takımın da adını girin.');
return;
}
document.getElementById('analysisHomeTeam').textContent = homeTeamName;
document.getElementById('analysisAwayTeam').textContent = awayTeamName;
document.getElementById('analysisMatchInfo').textContent = 'Manuel Analiz';
// For manual analysis, we'll try to find team IDs from current matches or use a generic approach
// Since we don't have a search endpoint in the free tier easily accessible without team IDs,
// we'll simulate the analysis with placeholder data or try to match from existing matches
// Try to find matching teams in current matches
const homeMatch = currentMatches.find(m =>
m.homeTeam.name.toLowerCase().includes(homeTeamName.toLowerCase()) ||
m.awayTeam.name.toLowerCase().includes(homeTeamName.toLowerCase())
);
const awayMatch = currentMatches.find(m =>
m.homeTeam.name.toLowerCase().includes(awayTeamName.toLowerCase()) ||
m.awayTeam.name.toLowerCase().includes(awayTeamName.toLowerCase())
);
if (homeMatch && awayMatch) {
const homeId = homeMatch.homeTeam.name.toLowerCase().includes(homeTeamName.toLowerCase()) ?
homeMatch.homeTeam.id : homeMatch.awayTeam.id;
const awayId = awayMatch.homeTeam.name.toLowerCase().includes(awayTeamName.toLowerCase()) ?
awayMatch.homeTeam.id : awayMatch.awayTeam.id;
await performAnalysis(homeId, awayId, homeTeamName, awayTeamName);
} else {
// If teams not found in current matches, show alert
alert('Takımlar mevcut maç listesinde bulunamadı. Lütfen listeden bir maç seçin veya geçerli takım adları girin.');
}
}
// Perform analysis for two teams
async function performAnalysis(homeTeamId, awayTeamId, homeTeamName, awayTeamName) {
showAnalysisLoading(true);
try {
// Fetch last 5 matches for both teams
const [homeMatches, awayMatches] = await Promise.all([
fetchTeamMatches(homeTeamId),
fetchTeamMatches(awayTeamId)
]);
// Calculate statistics
const homeStats = calculateTeamStats(homeMatches, homeTeamId);
const awayStats = calculateTeamStats(awayMatches, awayTeamId);
// Calculate predictions
const predictions = calculatePredictions(homeStats, awayStats);
// Update UI
updateAnalysisUI(homeStats, awayStats, predictions, homeTeamName, awayTeamName);
// Fetch and display H2H if possible
await fetchHeadToHead(homeTeamId, awayTeamId);
// Show analysis card
document.getElementById('analysisCard').style.display = 'block';
document.getElementById('emptyAnalysisCard').style.display = 'none';
} catch (error) {
console.error('Analysis error:', error);
showError('Analiz yapılırken bir hata oluştu.');
} finally {
showAnalysisLoading(false);
}
}
// Fetch team matches (finished matches)
async function fetchTeamMatches(teamId) {
const today = new Date();
const lastMonth = new Date(today);
lastMonth.setMonth(today.getMonth() - 3); // Look back 3 months to ensure we get 5 matches
const dateFrom = formatDateForAPI(lastMonth);
const dateTo = formatDateForAPI(today);
const response = await fetch(`${API_BASE_URL}/teams/${teamId}/matches?dateFrom=${dateFrom}&dateTo=${dateTo}&status=FINISHED`, {
headers: {
'X-Auth-Token': API_TOKEN
}
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
return data.matches ? data.matches.slice(0, 5) : []; // Last 5 matches
}
// Calculate team statistics from matches
function calculateTeamStats(matches, teamId) {
if (!matches || matches.length === 0) {
return {
matches: [],
avgGoals: 0,
avgConceded: 0,
avgPoints: 0,
form: '',
goalsScored: [],
goalsConceded: [],
results: []
};
}
let totalGoals = 0;
let totalConceded = 0;
let totalPoints = 0;
let form = '';
const goalsScored = [];
const goalsConceded = [];
const results = [];
matches.forEach(match => {
const isHome = match.homeTeam.id == teamId;
const teamScore = isHome ? match.score.fullTime.home : match.score.fullTime.away;
const opponentScore = isHome ? match.score.fullTime.away : match.score.fullTime.home;
totalGoals += teamScore;
totalConceded += opponentScore;
goalsScored.push(teamScore);
goalsConceded.push(opponentScore);
let result;
if (teamScore > opponentScore) {
totalPoints += 3;
result = 'W';
form += 'G-';
} else if (teamScore === opponentScore) {
totalPoints += 1;
result = 'D';
form += 'B-';
} else {
result = 'L';
form += 'M-';
}
results.push(result);
});
const matchCount = matches.length;
form = form.slice(0, -1); // Remove last dash
return {
matches: matches,
avgGoals: (totalGoals / matchCount).toFixed(2),
avgConceded: (totalConceded / matchCount).toFixed(2),
avgPoints: (totalPoints / matchCount).toFixed(2),
form: form,
goalsScored: goalsScored.reverse(), // Most recent last
goalsConceded: goalsConceded.reverse(),
results: results.reverse()
};
}
// Calculate prediction probabilities
function calculatePredictions(homeStats, awayStats) {
// Simple weighted algorithm
// Factors: Points average (40%), Goals scored/conceded difference (30%), Home advantage (20%), Recent form (10%)
// Normalize stats (handle empty data)
const homePoints = parseFloat(homeStats.avgPoints) || 1.5;
const awayPoints = parseFloat(awayStats.avgPoints) || 1.5;
const homeGoals = parseFloat(homeStats.avgGoals) || 1;
const awayGoals = parseFloat(awayStats.avgGoals) || 1;
const homeConceded = parseFloat(homeStats.avgConceded) || 1;
const awayConceded = parseFloat(awayStats.avgConceded) || 1;
// Points factor (40%)
const totalPoints = homePoints + awayPoints;
const homePointsProb = (homePoints / totalPoints) * 40;
const awayPointsProb = (awayPoints / totalPoints) * 40;
// Goals factor (30%) - based on attack vs defense
const homeAttackStrength = homeGoals / (homeGoals + awayConceded);
const awayAttackStrength = awayGoals / (awayGoals + homeConceded);
const totalAttack = homeAttackStrength + awayAttackStrength;
const homeGoalsProb = (homeAttackStrength / totalAttack) * 30;
const awayGoalsProb = (awayAttackStrength / totalAttack) * 30;
// Home advantage (20%)
const homeAdvantage = 12; // 60% of 20%
const awayDisadvantage = 8; // 40% of 20%
// Form factor (10%) - based on last 5 results
const homeFormScore = calculateFormScore(homeStats.results);
const awayFormScore = calculateFormScore(awayStats.results);
const totalForm = homeFormScore + awayFormScore || 2;
const homeFormProb = (homeFormScore / totalForm) * 10;
const awayFormProb = (awayFormScore / totalForm) * 10;
// Calculate raw probabilities
let homeWin = homePointsProb + homeGoalsProb + homeAdvantage + homeFormProb;
let awayWin = awayPointsProb + awayGoalsProb + awayDisadvantage + awayFormProb;
// Normalize to ensure they sum to less than 100, leaving room for draw
const totalWin = homeWin + awayWin;
const normalizationFactor = 85 / totalWin; // Reserve 15% for draw minimum
homeWin = homeWin * normalizationFactor;
awayWin = awayWin * normalizationFactor;
// Draw probability based on how close the teams are
const diff = Math.abs(homeWin - awayWin);
let draw = 15 + (diff < 10 ? 10 : 0) - (diff > 20 ? 5 : 0);
// Adjust win probabilities to ensure sum is 100
const remaining = 100 - draw;
const winTotal = homeWin + awayWin;
homeWin = (homeWin / winTotal) * remaining;
awayWin = (awayWin / winTotal) * remaining;
return {
homeWin: Math.round(homeWin),
draw: Math.round(draw),
awayWin: Math.round(awayWin)
};
}
// Calculate form score from results array (W, D, L)
function calculateFormScore(results) {
if (!results || results.length === 0) return 1;
let score = 0;
// Weight recent matches more
const weights = [1, 1.1, 1.2, 1.3, 1.4];
results.forEach((result, index) => {
const weight = weights[index] || 1;
if (result === 'W') score += 3 * weight;
else if (result === 'D') score += 1 * weight;
else score += 0;
});
return score / results.length;
}
// Update analysis UI
function updateAnalysisUI(homeStats, awayStats, predictions, homeName, awayName) {
// Update prediction bars
document.getElementById('homeWinLabel').textContent = `${homeName} Kazanır`;
document.getElementById('awayWinLabel').textContent = `${awayName} Kazanır`;
document.getElementById('homeWinPercent').textContent = `${predictions.homeWin}%`;
document.getElementById('drawPercent').textContent = `${predictions.draw}%`;
document.getElementById('awayWinPercent').textContent = `${predictions.awayWin}%`;
// Animate bars
setTimeout(() => {
document.getElementById('homeWinBar').style.width = `${predictions.homeWin}%`;
document.getElementById('drawBar').style.width = `${predictions.draw}%`;
document.getElementById('awayWinBar').style.width = `${predictions.awayWin}%`;
}, 100);
// Update stats
document.getElementById('homeStatsTitle').textContent = `${homeName} İstatistikleri`;
document.getElementById('awayStatsTitle').textContent = `${awayName} İstatistikleri`;
document.getElementById('homeAvgGoals').textContent = homeStats.avgGoals;
document.getElementById('homeAvgConceded').textContent = homeStats.avgConceded;
document.getElementById('homeAvgPoints').textContent = homeStats.avgPoints;
document.getElementById('awayAvgGoals').textContent = awayStats.avgGoals;
document.getElementById('awayAvgConceded').textContent = awayStats.avgConceded;
document.getElementById('awayAvgPoints').textContent = awayStats.avgPoints;
// Update form badges
document.getElementById('homeForm').innerHTML = createFormBadges(homeStats.form);
document.getElementById('awayForm').innerHTML = createFormBadges(awayStats.form);
// Update charts
updateCharts(homeStats, awayStats, homeName, awayName);
}
// Create form badges HTML
function createFormBadges(formString) {
if (!formString) return '-';
const parts = formString.split('-');
let html = '';
parts.forEach(part => {
let className = '';
let text = '';
switch(part) {
case 'G':
className = 'form-win';
text = 'G';
break;
case 'B':
className = 'form-draw';
text = 'B';
break;
case 'M':
className = 'form-loss';
text = 'M';
break;
}
html += `<span class="team-form-badge ${className}">${text}</span>`;
});
return html;
}
// Update charts
function updateCharts(homeStats, awayStats, homeName, awayName) {
// Destroy existing charts if they exist
if (homeTeamChart) homeTeamChart.destroy();
if (awayTeamChart) awayTeamChart.destroy();
// Home team chart
const homeCtx = document.getElementById('homeTeamChart').getContext('2d');
homeTeamChart = new Chart(homeCtx, {
type: 'bar',
data: {
labels: ['Maç 1', 'Maç 2', 'Maç 3', 'Maç 4', 'Maç 5 (Son)'],
datasets: [{
label: 'Atılan Gol',
data: homeStats.goalsScored,
backgroundColor: 'rgba(59, 130, 246, 0.7)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}, {
label: 'Yenilen Gol',
data: homeStats.goalsConceded,
backgroundColor: 'rgba(239, 68, 68, 0.7)',
borderColor: 'rgba(239, 68, 68, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: `${homeName} - Son 5 Maç`
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
// Away team chart
const awayCtx = document.getElementById('awayTeamChart').getContext('2d');
awayTeamChart = new Chart(awayCtx, {
type: 'bar',
data: {
labels: ['Maç 1', 'Maç 2', 'Maç 3', 'Maç 4', 'Maç 5 (Son)'],
datasets: [{
label: 'Atılan Gol',
data: awayStats.goalsScored,
backgroundColor: 'rgba(59, 130, 246, 0.7)',
borderColor: 'rgba(59, 130, 246, 1)',
borderWidth: 1
}, {
label: 'Yenilen Gol',
data: awayStats.goalsConceded,
backgroundColor: 'rgba(239, 68, 68, 0.7)',
borderColor: 'rgba(239, 68, 68, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: `${awayName} - Son 5 Maç`
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
// Fetch head to head matches
async function fetchHeadToHead(team1Id, team2Id) {
try {
// Note: H2H endpoint might require different permissions in free tier
// We'll try to find H2H from the matches we already have or fetch recent matches between them
const today = new Date();
const lastYear = new Date(today);
lastYear.setFullYear(today.getFullYear() - 1);
const response = await fetch(`${API_BASE_URL}/matches?dateFrom=${formatDateForAPI(lastYear)}&dateTo=${formatDateForAPI(today)}&status=FINISHED`, {
headers: {
'X-Auth-Token': API_TOKEN
}
});
if (!response.ok) return;
const data = await response.json();
const allMatches = data.matches || [];
// Filter matches between these two teams
const h2hMatches = allMatches.filter(m =>
(m.homeTeam.id == team1Id && m.awayTeam.id == team2Id) ||
(m.homeTeam.id == team2Id && m.awayTeam.id == team1Id)
).slice(0, 5); // Last 5 H2H
if (h2hMatches.length > 0) {
displayH2H(h2hMatches);
} else {
document.getElementById('h2hSection').style.display = 'none';
}
} catch (error) {
console.error('H2H fetch error:', error);
document.getElementById('h2hSection').style.display = 'none';
}
}
// Display H2H matches
function displayH2H(matches) {
const tbody = document.getElementById('h2hTableBody');
tbody.innerHTML = '';
matches.forEach(match => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${formatDateForDisplay(match.utcDate)}</td>
<td>${match.homeTeam.name}</td>
<td class="fw-bold">${match.score.fullTime.home} - ${match.score.fullTime.away}</td>
<td>${match.awayTeam.name}</td>
`;
tbody.appendChild(row);
});
document.getElementById('h2hSection').style.display = 'block';
}
// Refresh current analysis
async function refreshCurrentAnalysis() {
if (selectedMatch) {
await analyzeMatch(selectedMatch.id);
}
}
// Refresh all data
async function refreshData() {
await fetchUpcomingMatches();
if (selectedMatch) {
await refreshCurrentAnalysis();
}
}
// Auto refresh every 60 seconds
function startAutoRefresh() {
// Clear existing intervals if any
if (autoRefreshInterval) clearInterval(autoRefreshInterval);
if (countdownInterval) clearInterval(countdownInterval);
countdownValue = 60;
updateCountdown();
countdownInterval = setInterval(() => {
countdownValue--;
updateCountdown();
if (countdownValue <= 0) countdownValue = 60;
}, 1000);
autoRefreshInterval = setInterval(() => {
refreshData();
countdownValue = 60;
}, 60000);
}
function updateCountdown() {
document.getElementById('nextUpdateCountdown').textContent = `Sonraki güncelleme: ${countdownValue}s`;
}
// Update last update time display
function updateLastUpdateTime() {
const now = new Date();
lastUpdateTime.innerHTML = `<i class="fas fa-clock me-1"></i>Son Güncelleme: ${now.toLocaleTimeString('tr-TR')}`;
}
// Show/hide loading states
function showLoading(show) {
matchesLoading.style.display = show ? 'block' : 'none';
}
function showAnalysisLoading(show) {