// ==UserScript== // @name MZ - Unlucky // @namespace douglaskampl // @version 2.727 // @description Finds unlucky teams // @author Douglas // @match https://www.managerzone.com/?p=league&type* // @grant GM_addStyle // @run-at document-idle // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/534000/MZ%20-%20Unlucky.user.js // @updateURL https://update.greasyfork.icu/scripts/534000/MZ%20-%20Unlucky.meta.js // ==/UserScript== (function () { 'use strict'; const DEBUG = false; const BATCH_SIZE = 5; const MAX_GK_LOOKUP_ATTEMPTS = 10; GM_addStyle(`@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap'); @keyframes textGlitch { 0% { transform: translate(0); text-shadow: 0 0 8px rgba(255, 106, 193, 0.8); } 25% { text-shadow: -1px 1px 8px rgba(22, 242, 242, 0.8), 1px -1px 8px rgba(255, 106, 193, 0.8); } 50% { text-shadow: 1px -1px 8px rgba(22, 242, 242, 0.8), -1px 1px 8px rgba(255, 106, 193, 0.8); } 75% { text-shadow: -1px 0 8px rgba(22, 242, 242, 0.8), 1px 0 8px rgba(255, 106, 193, 0.8); } 100% { transform: translate(0); text-shadow: 0 0 8px rgba(255, 106, 193, 0.8); } } .pulse-dot { display: inline-block; width: 8px; height: 8px; background-color: #ff6ac1; border-radius: 50%; margin-right: 10px; animation: pulse 1.5s infinite ease-in-out; } @keyframes pulse { 0% { transform: scale(0.8); opacity: 0.5; } 50% { transform: scale(1.2); opacity: 1; } 100% { transform: scale(0.8); opacity: 0.5; } } @keyframes modalFadeIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } } @keyframes glow { 0% { box-shadow: 0 0 5px rgba(255, 0, 255, 0.5); } 50% { box-shadow: 0 0 20px rgba(0, 255, 255, 0.8); } 100% { box-shadow: 0 0 5px rgba(255, 0, 255, 0.5); } } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } #unluckyModal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); display: flex; justify-content: center; align-items: center; z-index: 9999; opacity: 0; animation: modalFadeIn 0.3s ease-out forwards; font-size: 16px; font-family: 'Inter', sans-serif; } #unluckyModalContent { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); background-size: 200% 200%; animation: gradientShift 15s ease infinite; padding: 25px; border-radius: 10px; width: 700px; max-width: 90vw; max-height: 85vh; overflow-y: auto; box-shadow: 0 0 30px rgba(138, 43, 226, 0.6); color: #fff; border: 1px solid rgba(255, 255, 255, 0.1); transform: translateY(20px); transition: transform 0.3s ease-out; box-sizing: border-box; } #unluckyModal:hover #unluckyModalContent { transform: translateY(0); } #unluckyModalHeader { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 1px solid rgba(255, 105, 180, 0.3); padding-bottom: 10px; } #unluckyModalTitle { font-size: 24px; font-weight: bold; color: #ff6ac1; text-shadow: 0 0 10px rgba(255, 105, 180, 0.7); font-family: 'Orbitron', sans-serif; letter-spacing: 2px; text-transform: uppercase; } #unluckyModalClose { cursor: pointer; font-size: 24px; color: #16f2f2; transition: all 0.2s; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; } #unluckyModalClose:hover { color: #ff6ac1; transform: rotate(90deg); background-color: rgba(255, 255, 255, 0.1); } .unluckyOption { margin: 15px 0; padding: 12px 20px; width: 100%; box-sizing: border-box; text-align: center; background: linear-gradient(to right, #614385, #516395); border: none; border-radius: 5px; cursor: pointer; color: white; font-weight: 500; font-size: 17px; transition: all 0.3s ease; position: relative; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); max-width: 100%; font-family: 'Inter', sans-serif; } .unluckyOption:before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: 0.5s; } .unluckyOption:hover { transform: translateY(-3px); box-shadow: 0 7px 14px rgba(0, 0, 0, 0.4); animation: glow 1.5s infinite; } .unluckyOption:hover:before { left: 100%; } .unluckyOption:active { transform: translateY(1px); } #unluckyResults { margin-top: 20px; max-height: 550px; overflow-y: auto; padding: 15px; border: 1px solid rgba(85, 213, 219, 0.3); display: none; background-color: rgba(0, 0, 0, 0.3); border-radius: 5px; color: #f0f0f0; font-family: 'Inter', sans-serif; transition: all 0.3s ease; } #unluckyResults p { margin: 5px 0; line-height: 1.5; } #unluckyResults::-webkit-scrollbar { width: 8px; } #unluckyResults::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.2); border-radius: 10px; } #unluckyResults::-webkit-scrollbar-thumb { background: linear-gradient(180deg, #ff6ac1, #7a4feb); border-radius: 10px; } #leftmenu_unlucky a { transition: all 0.3s ease; display: inline-block; font-family: 'Inter', sans-serif; } #leftmenu_unlucky a:hover { color: #ff6ac1 !important; text-shadow: 0 0 5px rgba(255, 105, 180, 0.7); transform: translateX(3px); } .team-stats { margin-bottom: 12px; padding: 10px; border-radius: 4px; border-left: 3px solid #ff6ac1; background-color: rgba(255, 255, 255, 0.05); font-size: 15px; font-family: 'Inter', sans-serif; } .stat-card { margin-bottom: 15px; padding: 15px; border-radius: 8px; background-color: rgba(0, 0, 0, 0.3); box-shadow: 0 0 10px rgba(138, 43, 226, 0.3); font-family: 'Inter', sans-serif; } .stat-card-title { margin-bottom: 10px; padding-bottom: 8px; font-size: 19px; font-weight: bold; color: #16f2f2; border-bottom: 1px solid rgba(255, 106, 193, 0.5); } .stat-item { margin: 10px 0; padding: 10px; border-radius: 4px; display: flex; align-items: center; justify-content: space-between; background-color: rgba(255, 255, 255, 0.05); font-size: 15px; flex-wrap: wrap; } .stat-item:hover { background-color: rgba(255, 255, 255, 0.1); } .stat-value { font-weight: bold; color: #ff6ac1; margin-left: auto; padding-left: 10px; flex-shrink: 0; } .stat-rank { display: inline-block; width: 24px; height: 24px; margin-right: 10px; background: linear-gradient(to right, #614385, #516395); border-radius: 50%; text-align: center; line-height: 24px; font-size: 13px; } .back-button { margin-top: 15px; padding: 8px 15px; background: linear-gradient(to right, #516395, #614385); border: none; border-radius: 4px; color: white; cursor: pointer; transition: all 0.3s ease; font-size: 15px; font-family: 'Inter', sans-serif; } .back-button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } .pagination { display: flex; justify-content: center; margin-top: 15px; } .pagination-button { padding: 6px 12px; margin: 0 5px; border: none; border-radius: 4px; background: linear-gradient(to right, #516395, #614385); color: white; cursor: pointer; transition: all 0.3s ease; font-family: 'Inter', sans-serif; } .pagination-button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } .pagination-button.active { background: linear-gradient(to right, #ff6ac1, #ff6ac1); } .league-selector { padding: 10px; margin-bottom: 10px; background-color: rgba(0, 0, 0, 0.2); border-radius: 5px; } .league-selector-title { margin-bottom: 10px; color: #16f2f2; font-size: 17px; } .league-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; } .league-item { padding: 5px 8px; text-align: center; background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; cursor: pointer; transition: all 0.2s; font-size: 15px; display: flex; align-items: center; justify-content: center; } .league-item:hover { background-color: rgba(255, 106, 193, 0.3); } .league-item.active { background-color: rgba(255, 106, 193, 0.5); } .tab-container { display: flex; margin-bottom: 15px; } .tab { padding: 10px 20px; background: rgba(255, 255, 255, 0.1); border: none; color: white; cursor: pointer; transition: all 0.3s ease; font-size: 16px; font-family: 'Inter', sans-serif; } .tab:first-child { border-radius: 5px 0 0 5px; } .tab:last-child { border-radius: 0 5px 5px 0; } .tab.active { background: linear-gradient(to right, #ff6ac1, #7a4feb); } .tab-content { display: none; } .tab-content.active { display: block; } .toggle-matches { cursor: pointer; color: #16f2f2; text-decoration: underline; margin-left: 5px; font-size: 0.95em; } .toggle-matches:hover { color: #ff6ac1; } .match-list { display: none; margin-top: 10px; padding: 8px; background-color: rgba(0, 0, 0, 0.2); border-radius: 5px; } .match-item { display: flex; justify-content: space-between; align-items: center; padding: 8px; margin-bottom: 5px; border-radius: 3px; background-color: rgba(255, 255, 255, 0.05); transition: all 0.2s; font-size: 14px; } .match-item:hover { background-color: rgba(255, 255, 255, 0.1); } .match-link { color: #fff; text-decoration: none; flex-grow: 1; } .match-link:hover { color: #ff6ac1; } .match-result { font-weight: bold; margin-left: 10px; } .match-result.win { color: #2ecc71; } .match-result.draw { color: #f39c12; } .match-result.loss { color: #e74c3c; } .match-stats { font-size: 0.9em; color: #bbb; } .info-icon { position: fixed; bottom: 20px; right: 20px; width: 40px; height: 40px; background-color: rgba(22, 242, 242, 0.8); color: #1a1a2e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; cursor: pointer; box-shadow: 0 0 15px rgba(22, 242, 242, 0.5); z-index: 10000; transition: all 0.3s ease; } .info-icon:hover { background-color: rgba(255, 106, 193, 0.8); box-shadow: 0 0 15px rgba(255, 106, 193, 0.5); transform: scale(1.1); } .info-tooltip { position: fixed; bottom: 70px; right: 20px; width: 350px; max-height: 70vh; overflow-y: auto; padding: 15px; background-color: rgba(26, 26, 46, 0.95); border: 1px solid rgba(22, 242, 242, 0.5); border-radius: 10px; color: white; font-size: 14px; line-height: 1.5; z-index: 10000; display: none; box-shadow: 0 0 20px rgba(22, 242, 242, 0.3); max-width: 80vw; font-family: 'Inter', sans-serif; } .info-tooltip h3 { color: #ff6ac1; margin-top: 0; margin-bottom: 10px; border-bottom: 1px solid rgba(255, 106, 193, 0.3); padding-bottom: 5px; font-family: 'Orbitron', sans-serif; } .info-tooltip p { margin: 10px 0; } .info-tooltip .formula { background-color: rgba(0, 0, 0, 0.2); padding: 8px; border-radius: 5px; margin: 10px 0; border-left: 3px solid #16f2f2; font-family: 'Courier New', monospace; } .info-tooltip .formula-explanation { margin-left: 15px; margin-bottom: 15px; color: #ddd; } .league-link { color: #16f2f2; text-decoration: none; transition: all 0.2s; display: inline-flex; align-items: center; } .league-link:hover { color: #ff6ac1; text-decoration: none; } .league-link:hover .league-label { background-color: rgba(255, 106, 193, 0.4); border-color: rgba(255, 106, 193, 0.7); } .loading-stage { margin-left: 10px; color: #ff6ac1; font-style: italic; font-size: 0.9em; } .loading-progress { margin-left: 10px; color: #16f2f2; } .league-range-input { display: flex; margin: 15px 0; gap: 10px; align-items: center; } .league-range-input input { flex: 1; padding: 8px; border-radius: 4px; border: none; background-color: rgba(255, 255, 255, 0.1); color: white; outline: none; } .league-input { width: 100%; padding: 10px; border-radius: 4px; margin-bottom: 15px; border: none; background-color: rgba(255, 255, 255, 0.1); color: white; outline: none; font-family: 'Inter', sans-serif; } .league-entry-container { margin-bottom: 20px; } .league-entry-form { display: flex; gap: 10px; margin-bottom: 10px; align-items: center; } .league-entry-form input, .league-entry-form select { flex: 1; padding: 8px 12px; border: none; border-radius: 4px; background-color: rgba(255, 255, 255, 0.1); color: lightgray; outline: none; } .league-entry-form select { cursor: pointer; } .league-entry-form .add-button { width: 36px; height: 36px; display: flex; justify-content: center; align-items: center; background: linear-gradient(to right, #614385, #516395); border: none; border-radius: 50%; color: white; font-size: 20px; cursor: pointer; transition: all 0.3s ease; flex-shrink: 0; } .league-entry-form .add-button:hover { transform: scale(1.1); box-shadow: 0 0 10px rgba(138, 43, 226, 0.5); } .league-list { max-height: 200px; overflow-y: auto; padding: 10px; background-color: rgba(0, 0, 0, 0.2); border-radius: 5px; margin-bottom: 15px; } .league-list-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; margin-bottom: 6px; background-color: rgba(255, 255, 255, 0.05); border-radius: 4px; } .league-list-item:last-child { margin-bottom: 0; } .league-list-item .remove-button { background: none; border: none; color: #ff6ac1; cursor: pointer; font-size: 16px; transition: all 0.2s; } .league-list-item .remove-button:hover { transform: scale(1.2); } .league-label { background-color: rgba(22, 242, 242, 0.15); border: 1px solid rgba(22, 242, 242, 0.4); color: #e0e0e0; padding: 2px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 500; margin-left: 6px; white-space: nowrap; transition: all 0.2s; display: inline-block; }`); const CURRENCIES = { "R$": 2.62589, EUR: 9.1775, USD: 7.4234, "点": 1, SEK: 1, NOK: 1.07245, DKK: 1.23522, GBP: 13.35247, CHF: 5.86737, RUB: 0.26313, CAD: 5.70899, AUD: 5.66999, MZ: 1, MM: 1, PLN: 1.95278, ILS: 1.6953, INR: 0.17, THB: 0.17079, ZAR: 1.23733, SKK: 0.24946, BGN: 4.70738, MXN: 0.68576, ARS: 2.64445, BOB: 0.939, UYU: 0.256963, PYG: 0.001309, ISK: 0.10433, SIT: 0.03896, JPY: 0.06, }; const LOG_COLORS = { info: "#16f2f2", warn: "#ff6ac1", error: "#ff0000", success: "#00ff00", debug: "#f39c12", gk: "#7a4feb" }; const LEAGUE_TYPES = [ { value: "senior", label: "Senior" }, { value: "u23", label: "U23" }, { value: "u23_world", label: "U23 World" }, { value: "u21", label: "U21" }, { value: "u21_world", label: "U21 World" }, { value: "u18", label: "U18" }, { value: "u18_world", label: "U18 World" } ]; const REGIONAL_LEAGUE_NAMES = { 727: "Americas", 1: "Argentina", 122: "Brazil", 848: "Central Europe", 969: "Iberia", 1090: "Mediterranean", 1211: "Northern Europe", 243: "Poland", 364: "Romania", 485: "Sweden", 606: "Turkey", 1332: "World" }; const teamPlayersCache = {}; const teamGoalkeeperCache = {}; const xmlCache = {}; const matchCache = {}; const gkLogging = { attempts: 0, successes: 0, failures: 0, reasons: {}, teamDetails: {}, conversionDetails: {}, processedTeams: new Set(), teamsWithGk: new Set(), matchesUsed: new Set() }; function log(msg, type = "info", obj = null) { if (!DEBUG) return; const style = `color: ${LOG_COLORS[type] || LOG_COLORS.info}; font-weight: bold;`; if (obj !== null) { console.log(`%c[MZ-UNLUCKY] ${msg}`, style, obj); } else { console.log(`%c[MZ-UNLUCKY] ${msg}`, style); } } function displayGkDebugInfo() { log("===== GOALKEEPER DEBUGGING INFORMATION =====", "gk"); log(`Total attempts: ${gkLogging.attempts}`, "gk"); log(`Successes: ${gkLogging.successes}`, "gk"); log(`Failures: ${gkLogging.failures}`, "gk"); log(`Success rate: ${((gkLogging.successes / Math.max(gkLogging.attempts, 1)) * 100).toFixed(2)}%`, "gk"); log(`Teams processed: ${gkLogging.processedTeams.size}`, "gk"); log(`Teams with goalkeeper data: ${gkLogging.teamsWithGk.size}`, "gk"); log(`Total matches used: ${gkLogging.matchesUsed.size}`, "gk"); log("Failure reasons:", "gk", gkLogging.reasons); log("Team details:", "gk", gkLogging.teamDetails); log("Currency conversion details:", "gk", gkLogging.conversionDetails); const currencyCount = {}; for (const teamId in gkLogging.teamDetails) { const details = gkLogging.teamDetails[teamId]; if (details.success && details.currency) { currencyCount[details.currency] = (currencyCount[details.currency] || 0) + 1; } } log("GKs by currency:", "gk", currencyCount); const currencyAvg = {}; for (const teamId in gkLogging.teamDetails) { const details = gkLogging.teamDetails[teamId]; if (details.success && details.currency) { if (!currencyAvg[details.currency]) { currencyAvg[details.currency] = { count: 0, total: 0, totalUsd: 0 }; } currencyAvg[details.currency].count++; currencyAvg[details.currency].total += details.originalValue; currencyAvg[details.currency].totalUsd += details.valueInUsd; } } for (const currency in currencyAvg) { const data = currencyAvg[currency]; data.average = data.total / data.count; data.averageUsd = data.totalUsd / data.count; } log("Average GK values by currency:", "gk", currencyAvg); const topGks = Object.entries(gkLogging.teamDetails) .filter(([_, details]) => details.success) .sort((a, b) => b[1].valueInUsd - a[1].valueInUsd) .slice(0, 5); log("Top 5 most valuable goalkeepers:", "gk", topGks); log("================================================", "gk"); } function getCurrentLeagueId() { const url = window.location.href; const match = url.match(/sid=(\d+)/); return match ? match[1] : null; } function getCurrentLeagueType() { const url = window.location.href; const match = url.match(/type=([^&]+)/); return match ? match[1] : null; } function getDivisionDisplayName(sid, leagueType) { const sidNum = parseInt(sid); if (!sidNum) return `League ${sid}`; if (leagueType && leagueType.includes('world')) { if (sidNum === 1) return "Top Series"; let level = 0; let levelStartSid = 1; let leaguesInLevel = 1; while (sidNum >= levelStartSid + leaguesInLevel) { levelStartSid += leaguesInLevel; level++; leaguesInLevel *= 3; } if (level > 0) { const indexInLevel = sidNum - levelStartSid + 1; return `Div ${level}.${indexInLevel}`; } } else { const regionalName = REGIONAL_LEAGUE_NAMES[sidNum]; if (regionalName) { return regionalName; } } return `League ${sid}`; } function makeRequest(url) { log(`Making request to: ${url}`, "debug"); return fetch(url) .then(response => { if (!response.ok) { log(`Request failed with status ${response.status}`, "error"); throw new Error(`Request failed with status ${response.status}`); } return response.text(); }); } function convertToUsd(value, currency) { gkLogging.conversionDetails[currency] = gkLogging.conversionDetails[currency] || []; log(`Converting value from ${currency}: ${value}`, "debug"); if (currency === 'USD') { log(`No conversion needed for USD value: ${value}`, "gk"); gkLogging.conversionDetails[currency].push({ original: value, converted: value, rate: 1, formula: 'No conversion (USD)' }); return value; } const conversionRate = CURRENCIES[currency] || 1; if (!CURRENCIES[currency]) { log(`Unknown currency: ${currency}, defaulting rate to 1`, "warn"); } let valueInUsd; if (currency === 'SEK') { valueInUsd = (value / CURRENCIES.USD); log(`Converting from SEK: ${value} SEK = ${valueInUsd} USD (${value} / ${CURRENCIES.USD})`, "gk"); } else { const valueInSek = value * conversionRate; valueInUsd = valueInSek / CURRENCIES.USD; log(`Converting from ${currency}: ${value} ${currency} = ${valueInSek} SEK = ${valueInUsd} USD (${value} * ${conversionRate} / ${CURRENCIES.USD})`, "gk"); } gkLogging.conversionDetails[currency].push({ original: value, converted: valueInUsd, rate: conversionRate, usdRate: CURRENCIES.USD, formula: `(${value} * ${conversionRate}) / ${CURRENCIES.USD} = ${valueInUsd}` }); return valueInUsd; } function createModal() { const modal = document.createElement('div'); modal.id = 'unluckyModal'; modal.style.display = 'none'; const modalContent = document.createElement('div'); modalContent.id = 'unluckyModalContent'; const modalHeader = document.createElement('div'); modalHeader.id = 'unluckyModalHeader'; const modalTitle = document.createElement('div'); modalTitle.id = 'unluckyModalTitle'; modalTitle.innerHTML = 'アンラッキー 運命'; const modalClose = document.createElement('div'); modalClose.id = 'unluckyModalClose'; modalClose.textContent = '×'; modalClose.addEventListener('click', closeModal); modalHeader.appendChild(modalTitle); modalHeader.appendChild(modalClose); const currentOption = document.createElement('div'); currentOption.className = 'unluckyOption'; currentOption.innerHTML = '🔍 Current League'; currentOption.addEventListener('click', function() { findUnluckyTeams('current'); }); const specificOption = document.createElement('div'); specificOption.className = 'unluckyOption'; specificOption.innerHTML = '🎯 Specific League'; specificOption.addEventListener('click', function() { findUnluckyTeams('specific'); }); const allOption = document.createElement('div'); allOption.className = 'unluckyOption'; allOption.innerHTML = '🌐 All Leagues'; allOption.addEventListener('click', function() { findUnluckyTeams('all'); }); const resultsDiv = document.createElement('div'); resultsDiv.id = 'unluckyResults'; modalContent.appendChild(modalHeader); modalContent.appendChild(currentOption); modalContent.appendChild(specificOption); modalContent.appendChild(allOption); modalContent.appendChild(resultsDiv); modal.appendChild(modalContent); const infoIcon = document.createElement('div'); infoIcon.className = 'info-icon'; infoIcon.textContent = 'ℹ️'; infoIcon.title = 'Click for information about luck calculations'; const infoTooltip = document.createElement('div'); infoTooltip.className = 'info-tooltip'; infoTooltip.innerHTML = `

How Luck is Calculated

The Luck Index combines multiple factors to accurately measure how lucky or unlucky a team has been based on their match performance data:

Luck Index = Offensive Conversion Rate - Defensive Conversion Rate - GK Value Adjustment

Offensive Conversion Rate: (Goals Scored ÷ Shots on Target) × 100

Defensive Conversion Rate: (Goals Conceded ÷ Shots on Target Against) × 100

GK Value Adjustment: min(GK Value in USD ÷ 1,000,000, 5) × 0.5

A higher Luck Index indicates a luckier team (scoring from relatively few shots and/or conceding few goals despite facing many shots on target).

A lower Luck Index indicates an unlucky team (struggling to score despite creating chances and/or conceding frequently from relatively few shots).

The GK Value Adjustment factor accounts for goalkeeper quality. Teams with expensive goalkeepers are expected to have better defensive conversion rates, so this reduces their luck rating (as good defensive performance with a top goalkeeper is skill, not luck).

Additional metrics tracked:

Example calculation:
Team A has scored 15 goals from 50 shots on target (30% offensive conversion)
They've conceded 10 goals from 40 shots on target against (25% defensive conversion)
Their goalkeeper is worth $3M
GK Value Adjustment: min(3, 5) × 0.5 = 1.5
Luck Index = 30% - 25% - 1.5 = 3.5

`; infoIcon.addEventListener('click', function() { infoTooltip.style.display = infoTooltip.style.display === 'block' ? 'none' : 'block'; }); document.addEventListener('click', function(e) { if (e.target !== infoIcon && infoTooltip.style.display === 'block') { infoTooltip.style.display = 'none'; } }); modal.appendChild(infoIcon); modal.appendChild(infoTooltip); document.body.appendChild(modal); log("Modal created successfully", "success"); } function openUnluckyModal() { document.getElementById('unluckyModal').style.display = 'flex'; document.getElementById('unluckyResults').style.display = 'none'; document.getElementById('unluckyResults').innerHTML = ''; log("Modal opened", "info"); } function closeModal() { document.getElementById('unluckyModal').style.display = 'none'; const resultsDiv = document.getElementById('unluckyResults'); resultsDiv.style.display = 'none'; resultsDiv.innerHTML = ''; document.querySelectorAll('.unluckyOption').forEach(option => { option.style.display = 'block'; }); const infoTooltip = document.querySelector('.info-tooltip'); if (infoTooltip) { infoTooltip.style.display = 'none'; } log("Modal closed", "info"); } function showBackButton(resultsDiv) { const backButton = document.createElement('button'); backButton.className = 'back-button'; backButton.textContent = '← Back to Options'; backButton.addEventListener('click', function() { resultsDiv.style.display = 'none'; resultsDiv.innerHTML = ''; document.querySelectorAll('.unluckyOption').forEach(option => { option.style.display = 'block'; }); }); resultsDiv.appendChild(backButton); } function addUnluckyButton() { const leftNav = document.querySelector('ul.leftnav'); if (leftNav) { const li = document.createElement('li'); li.id = 'leftmenu_unlucky'; const button = document.createElement('a'); button.href = '#'; button.innerHTML = 'Unlucky '; button.addEventListener('click', function(e) { e.preventDefault(); openUnluckyModal(); }); li.appendChild(button); leftNav.appendChild(li); log("Added Unlucky button to left navigation", "success"); } else { const expanderMenuList = document.querySelector('#nmenu'); if (expanderMenuList) { const dt = document.createElement('dt'); dt.className = 'news'; dt.innerHTML = `
Unlucky
`; dt.querySelector('a').addEventListener('click', function(e) { e.preventDefault(); openUnluckyModal(); }); expanderMenuList.appendChild(dt); log("Added Unlucky button to expander menu", "success"); } else { log("Could not find any navigation to add button", "error"); } } } async function fetchTeamPlayers(teamId) { log(`Fetching team players for team ID: ${teamId}`, "info"); if (teamPlayersCache[teamId]) { log(`Cache hit for team ${teamId}`, "success"); return teamPlayersCache[teamId]; } const cacheKey = `team_${teamId}`; if (xmlCache[cacheKey]) { log(`XML cache hit for team ${teamId}`, "success"); return xmlCache[cacheKey]; } try { const url = `https://www.managerzone.com/xml/team_playerlist.php?sport_id=1&team_id=${teamId}`; log(`API request to: ${url}`, "debug"); const response = await fetch(url); if (!response.ok) { log(`API response not OK: ${response.status} ${response.statusText}`, "error"); return null; } const text = await response.text(); log(`Received ${text.length} bytes of XML data`, "debug"); const parser = new DOMParser(); const xmlDoc = parser.parseFromString(text, "text/xml"); const parserError = xmlDoc.querySelector("parsererror"); if (parserError) { log(`XML parsing error: ${parserError.textContent}`, "error"); return null; } const teamPlayersElement = xmlDoc.querySelector('TeamPlayers'); if (!teamPlayersElement) { log(`No TeamPlayers element found in XML for team ${teamId}`, "error"); return null; } const teamCurrency = teamPlayersElement.getAttribute('teamCurrency') || 'USD'; log(`Team ${teamId} currency: ${teamCurrency}`, "info"); const players = []; let players18Count = 0; const playerElements = xmlDoc.querySelectorAll('Player'); log(`Found ${playerElements.length} players for team ${teamId}`, "info"); playerElements.forEach(player => { const id = player.getAttribute('id'); const name = player.getAttribute('name'); const value = parseInt(player.getAttribute('value')) || 0; const junior = player.getAttribute('junior') === '1'; const shirtNo = player.getAttribute('shirtNo'); const age = parseInt(player.getAttribute('age')) || 0; if (age === 18) { players18Count++; } players.push({ id, name, value, junior, shirtNo, age }); if (shirtNo === "1") { log(`Potential GK detected in XML: ${name} (#${shirtNo})`, "gk", { id, name, value, teamCurrency }); } }); log(`Found ${players18Count} players aged 18 for team ${teamId}`, "info"); const result = { teamCurrency, players, players18Count }; teamPlayersCache[teamId] = result; xmlCache[cacheKey] = result; log(`Successfully processed team data for ${teamId}`, "success"); return result; } catch (error) { log(`Error fetching team players for ${teamId}: ${error.message}`, "error"); return null; } } async function processMatchComplete(matchId, teamIdMap = null) { log(`Processing match ${matchId} for complete data`, "info"); if (matchCache[matchId]) { log(`Cache hit for match ${matchId}`, "success"); return matchCache[matchId]; } try { const matchUrl = `https://www.managerzone.com/?p=match&sub=result&mid=${matchId}`; log(`Fetching match page: ${matchUrl}`, "debug"); const response = await fetch(matchUrl); if (!response.ok) { log(`Match page fetch failed: ${response.status} ${response.statusText}`, "error"); return null; } const html = await response.text(); log(`Received ${html.length} bytes of match HTML`, "debug"); const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const matchInfoWrapper = doc.getElementById('match-info-wrapper'); if (!matchInfoWrapper) { log(`No match info wrapper found for match ${matchId}`, "error"); return null; } const teamElements = matchInfoWrapper.querySelectorAll('a[href*="/?p=team&tid="]'); if (teamElements.length < 2) { log(`Not enough team links found for match ${matchId}, found ${teamElements.length}`, "error"); return null; } const homeTeamHref = teamElements[0].getAttribute('href') || ''; const awayTeamHref = teamElements[1].getAttribute('href') || ''; const homeTeamMatch = homeTeamHref.match(/tid=(\d+)/); const awayTeamMatch = awayTeamHref.match(/tid=(\d+)/); if (!homeTeamMatch || !awayTeamMatch) { log(`Failed to extract team IDs from match ${matchId}`, "error"); return null; } const homeTeam = { name: teamElements[0].textContent.trim(), id: homeTeamMatch[1] }; const awayTeam = { name: teamElements[1].textContent.trim(), id: awayTeamMatch[1] }; log(`Match: ${homeTeam.name} (${homeTeam.id}) vs ${awayTeam.name} (${awayTeam.id})`, "debug"); const teamTables = doc.querySelectorAll('.team-table.block'); log(`Found ${teamTables.length} team tables in match page`, "debug"); if (!teamTables || teamTables.length < 2) { log(`Insufficient team tables (${teamTables?.length || 0}) found for match ${matchId}`, "error"); return null; } const teamsInMatch = []; teamTables.forEach((table, index) => { const teamLink = table.querySelector('a[href*="tid="]'); if (teamLink) { const href = teamLink.getAttribute('href'); const tidMatch = href.match(/tid=(\d+)/); if (tidMatch) { const teamId = tidMatch[1]; const teamName = teamLink.textContent.trim(); teamsInMatch.push({ id: teamId, name: teamName, table: table, isHome: index === 0 }); } } }); const tacticBoard = matchInfoWrapper.querySelector('#tactic-board'); let matchFactsTable = null; if (tacticBoard && tacticBoard.nextElementSibling) { matchFactsTable = tacticBoard.nextElementSibling.querySelector('table.hitlist.statsLite.marker'); } if (!matchFactsTable) { log(`No match facts table found for match ${matchId}`, "error"); return null; } let homeGoals = 0, awayGoals = 0; let homeSoT = 0, awaySoT = 0; let homePossession = 0, awayPossession = 0; const statsRows = matchFactsTable.querySelectorAll('tbody tr'); if (statsRows.length > 0) { const homeTd = statsRows[0].querySelector('td:nth-child(2)'); const awayTd = statsRows[0].querySelector('td:nth-child(3)'); if (homeTd && awayTd) { homeGoals = parseInt(homeTd.textContent.trim()) || 0; awayGoals = parseInt(awayTd.textContent.trim()) || 0; } } if (statsRows.length > 7) { const homeTd = statsRows[7].querySelector('td:nth-child(2)'); const awayTd = statsRows[7].querySelector('td:nth-child(3)'); if (homeTd && awayTd) { homeSoT = parseInt(homeTd.textContent.trim()) || 0; awaySoT = parseInt(awayTd.textContent.trim()) || 0; } } if (statsRows.length > 8) { const homeTd = statsRows[8].querySelector('td:nth-child(2)'); const awayTd = statsRows[8].querySelector('td:nth-child(3)'); if (homeTd && awayTd) { const homeMatch = homeTd.textContent.trim().match(/(\d+)%/); const awayMatch = awayTd.textContent.trim().match(/(\d+)%/); homePossession = homeMatch ? parseInt(homeMatch[1]) : 50; awayPossession = awayMatch ? parseInt(awayMatch[1]) : 50; } } let homeResult = homeGoals > awayGoals ? 'W' : (homeGoals < awayGoals ? 'L' : 'D'); let awayResult = awayGoals > homeGoals ? 'W' : (awayGoals < homeGoals ? 'L' : 'D'); const homeUnlucky = homeSoT > awaySoT && homeResult !== 'W'; const awayUnlucky = awaySoT > homeSoT && awayResult !== 'W'; const homeLucky = homeSoT < awaySoT && homeResult === 'W'; const awayLucky = awaySoT < homeSoT && awayResult === 'W'; const homeConversion = homeSoT > 0 ? (homeGoals / homeSoT * 100).toFixed(1) : 0; const awayConversion = awaySoT > 0 ? (awayGoals / awaySoT * 100).toFixed(1) : 0; const matchData = { mid: matchId, teams: teamsInMatch, homeTeam: { ...homeTeam, goals: homeGoals, shotsOnTarget: homeSoT, possession: homePossession, conversionRate: homeConversion, result: homeResult, unlucky: homeUnlucky, lucky: homeLucky }, awayTeam: { ...awayTeam, goals: awayGoals, shotsOnTarget: awaySoT, possession: awayPossession, conversionRate: awayConversion, result: awayResult, unlucky: awayUnlucky, lucky: awayLucky }, doc: doc }; matchCache[matchId] = matchData; return matchData; } catch (error) { log(`Error processing match ${matchId}: ${error.message}`, "error"); return null; } } async function extractGoalkeeperData(matchData, teamIdMap) { if (!matchData) return null; gkLogging.attempts++; gkLogging.matchesUsed.add(matchData.mid); try { for (const team of matchData.teams) { if (teamGoalkeeperCache[team.id]) { log(`Team ${team.id} already has goalkeeper data, skipping`, "debug"); continue; } if (teamIdMap && !teamIdMap[team.id]) { log(`Team ${team.id} not in current league's team map, skipping`, "debug"); continue; } gkLogging.processedTeams.add(team.id); const teamData = await fetchTeamPlayers(team.id); if (!teamData) { log(`Failed to fetch team data for team ${team.id}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "no_team_data" }; continue; } const lineupTable = team.table.querySelector('table.hitlist.soccer.statsLite.marker'); if (!lineupTable) { log(`Lineup table not found for team ${team.id}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "lineup_table_not_found" }; continue; } const rows = lineupTable.querySelectorAll('tbody tr'); if (!rows || rows.length === 0) { log(`No player rows found in lineup table for team ${team.id}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "no_player_rows" }; continue; } const firstRow = rows[0]; const playerLink = firstRow.querySelector('td:nth-child(3) a'); if (!playerLink) { log(`No player link found in first row for team ${team.id}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "no_player_link" }; continue; } const playerHref = playerLink.getAttribute('href'); const pidMatch = playerHref.match(/pid=(\d+)/); if (!pidMatch) { log(`No player ID found in player link for team ${team.id}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "no_player_id" }; continue; } const goalkeeperId = pidMatch[1]; log(`Found goalkeeper ID ${goalkeeperId} for team ${team.id}`, "gk"); const goalkeeper = teamData.players.find(p => p.id === goalkeeperId); if (goalkeeper) { const currencyValue = teamData.teamCurrency; const originalValue = goalkeeper.value; log(`Goalkeeper found: ${goalkeeper.name} (ID: ${goalkeeper.id})`, "gk", { team: team.id, teamName: team.name, currency: currencyValue, value: originalValue }); const valueInUsd = convertToUsd(originalValue, currencyValue); log(`Goalkeeper value: ${originalValue} ${currencyValue} = ${valueInUsd.toFixed(2)} USD`, "gk"); const result = { ...goalkeeper, valueInUsd }; teamGoalkeeperCache[team.id] = result; gkLogging.successes++; gkLogging.teamsWithGk.add(team.id); gkLogging.teamDetails[team.id] = { success: true, goalkeeper: goalkeeper.name, originalValue: originalValue, currency: currencyValue, valueInUsd: valueInUsd }; log(`Successfully cached goalkeeper data for team ${team.id}`, "success"); } else { log(`Goalkeeper not found in team data for ID ${goalkeeperId}`, "error"); gkLogging.teamDetails[team.id] = { success: false, reason: "goalkeeper_not_in_team_data", goalkeeperId: goalkeeperId }; } } return true; } catch (error) { log(`Error extracting goalkeeper data from match: ${error.message}`, "error"); gkLogging.failures++; gkLogging.reasons["exception"] = (gkLogging.reasons["exception"] || 0) + 1; return false; } } async function fetchGoalkeepersMultiMatches(teams, matches, loadingEl = null) { log(`Starting goalkeeper data fetch using ${matches.length} matches for ${teams.length} teams`, "info"); gkLogging.processedTeams.clear(); gkLogging.teamsWithGk.clear(); gkLogging.matchesUsed.clear(); const teamIdMap = {}; teams.forEach(team => { teamIdMap[team.id] = team; }); log(`Team ID map created with ${Object.keys(teamIdMap).length} teams`, "debug"); const teamsNeedingGkData = teams.filter(team => !teamGoalkeeperCache[team.id]).map(team => team.id); if (teamsNeedingGkData.length === 0) { log("All teams already have goalkeeper data in cache", "success"); return; } log(`Teams needing goalkeeper data: ${teamsNeedingGkData.length}`, "info", teamsNeedingGkData); let matchesAttempted = 0; for (const match of matches) { if (gkLogging.teamsWithGk.size >= teams.length) { log(`Found goalkeeper data for all ${teams.length} teams, stopping match processing`, "success"); break; } if (matchesAttempted >= MAX_GK_LOOKUP_ATTEMPTS) { log(`Hit maximum match lookup limit (${MAX_GK_LOOKUP_ATTEMPTS})`, "warn"); break; } matchesAttempted++; if (loadingEl) { loadingEl.innerHTML = `
Fetching goalkeeper data Match ${matchesAttempted}/${Math.min(matches.length, MAX_GK_LOOKUP_ATTEMPTS)} (Found: ${gkLogging.teamsWithGk.size}/${teams.length} goalkeepers)`; } const homeTeamId = match.homeTeam.id; const awayTeamId = match.awayTeam.id; if (teamGoalkeeperCache[homeTeamId] && teamGoalkeeperCache[awayTeamId]) { log(`Skipping match ${match.mid}: both teams already have GK data`, "debug"); continue; } const matchData = await processMatchComplete(match.mid, teamIdMap); if (matchData) { await extractGoalkeeperData(matchData, teamIdMap); } log(`After processing match ${match.mid}: Found GK data for ${gkLogging.teamsWithGk.size}/${teams.length} teams`, "info"); } if (loadingEl) { loadingEl.innerHTML = `
Goalkeeper data collection complete (Found: ${gkLogging.teamsWithGk.size}/${teams.length} goalkeepers)`; } log(`Goalkeeper lookup complete. Processed ${matchesAttempted} matches.`, "success"); log(`Found goalkeeper data for ${gkLogging.teamsWithGk.size}/${teams.length} teams (${((gkLogging.teamsWithGk.size / teams.length) * 100).toFixed(1)}%)`, gkLogging.teamsWithGk.size > 0 ? "success" : "warn"); } function extractTeamData(html) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const teams = []; const rows = doc.querySelectorAll('table.nice_table tbody tr'); log(`Found ${rows.length} team rows in league table`, "debug"); rows.forEach(row => { const teamLinkElement = row.querySelector('td:nth-child(2) a[href*="tid="]'); if (teamLinkElement) { const teamName = teamLinkElement.textContent.trim(); const hrefMatch = teamLinkElement.getAttribute('href').match(/tid=(\d+)/); const teamId = hrefMatch ? hrefMatch[1] : null; if (teamId) { const positionElement = row.querySelector('td:first-child'); const position = positionElement ? positionElement.textContent.trim() : ''; const matches = row.querySelector('td:nth-child(3)').textContent.trim(); const wins = row.querySelector('td:nth-child(4)').textContent.trim(); const draws = row.querySelector('td:nth-child(5)').textContent.trim(); const losses = row.querySelector('td:nth-child(6)').textContent.trim(); const goalsFor = row.querySelector('td:nth-child(7)').textContent.trim(); const goalsAgainst = row.querySelector('td:nth-child(8)').textContent.trim(); const goalDiff = row.querySelector('td:nth-child(9)').textContent.trim(); const points = row.querySelector('td:nth-child(10)').textContent.trim(); const last6Element = row.querySelector('td:nth-child(11)'); const last6Results = []; if (last6Element) { const matchLinks = last6Element.querySelectorAll('a'); matchLinks.forEach(link => { const imgElement = link.querySelector('img'); if (imgElement) { const resultType = imgElement.getAttribute('src').includes('green') ? 'W' : imgElement.getAttribute('src').includes('yellow') ? 'D' : 'L'; const matchTitle = link.getAttribute('title') || ''; last6Results.push({ result: resultType, match: matchTitle }); } }); } teams.push({ id: teamId, name: teamName, position, matches: parseInt(matches) || 0, wins: parseInt(wins) || 0, draws: parseInt(draws) || 0, losses: parseInt(losses) || 0, goalsFor: parseInt(goalsFor) || 0, goalsAgainst: parseInt(goalsAgainst) || 0, goalDiff: parseInt(goalDiff) || 0, points: parseInt(points) || 0, last6: last6Results }); log(`Extracted team: ${teamName} (ID: ${teamId})`, "debug"); } } }); log(`Total teams extracted: ${teams.length}`, "info"); return teams; } function extractScheduleData(html, teams) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const matches = []; const teamNameToId = {}; teams.forEach(team => { teamNameToId[team.name] = team.id; }); const matchRows = doc.querySelectorAll('.hitlist tr'); log(`Found ${matchRows.length} match rows in schedule`, "debug"); matchRows.forEach(row => { const homeTeamCell = row.querySelector('td:nth-child(1)'); const resultCell = row.querySelector('td:nth-child(2) a'); const awayTeamCell = row.querySelector('td:nth-child(3)'); if (homeTeamCell && resultCell && awayTeamCell) { const homeTeamName = homeTeamCell.textContent.trim(); const awayTeamName = awayTeamCell.textContent.trim(); const result = resultCell.textContent.trim(); const matchHref = resultCell.getAttribute('href'); const midMatch = matchHref.match(/mid=(\d+)/); const mid = midMatch ? midMatch[1] : null; if (mid) { const homeTeamId = teamNameToId[homeTeamName] || null; const awayTeamId = teamNameToId[awayTeamName] || null; matches.push({ mid: mid, homeTeam: { name: homeTeamName, id: homeTeamId }, awayTeam: { name: awayTeamName, id: awayTeamId }, result: result }); if (!homeTeamId || !awayTeamId) { log(`Warning: Team ID missing for match ${mid}: ${homeTeamName} vs ${awayTeamName}`, "warn"); } } } }); log(`Total matches extracted: ${matches.length}`, "info"); return matches; } async function processLeagueData(leagueId, resultsDiv, allLeaguesData = null, leagueType = null) { if (!leagueType) { leagueType = getCurrentLeagueType(); if (!leagueType) { const errorMsg = "League type not found in URL and no type provided"; log(errorMsg, "error"); if (resultsDiv) { resultsDiv.innerHTML = `

Error: ${errorMsg}

`; showBackButton(resultsDiv); } return; } } const tableUrl = `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${leagueId}&tid=1&sport=soccer&sub=table`; const scheduleUrl = `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${leagueId}&tid=1&sport=soccer&sub=schedule`; const loadingEl = document.createElement('div'); loadingEl.style.display = 'flex'; loadingEl.style.justifyContent = 'center'; loadingEl.style.alignItems = 'center'; loadingEl.style.margin = '30px 0'; loadingEl.innerHTML = `
Loading league data...`; if (resultsDiv) { resultsDiv.innerHTML = ''; resultsDiv.appendChild(loadingEl); } try { loadingEl.innerHTML = `
Loading league data Step 1/3: Fetching league information`; log(`Processing league ID: ${leagueId} (Type: ${leagueType})`, "info"); const [tableHTML, scheduleHTML] = await Promise.all([makeRequest(tableUrl), makeRequest(scheduleUrl)]); loadingEl.innerHTML = `
Processing league data Step 2/3: Processing teams and schedule`; const teams = extractTeamData(tableHTML); const allMatches = extractScheduleData(scheduleHTML, teams); const playedMatches = allMatches.filter(match => /^\d+\s*-\s*\d+$/.test(match.result)); log(`Found ${teams.length} teams and ${playedMatches.length} played matches in league ${leagueId}`, "info"); const teamStats = {}; teams.forEach(team => { teamStats[team.id] = { name: team.name, shotsOnTarget: 0, goalsScored: 0, shotsOnTargetReceived: 0, goalsConceded: 0, matchesPlayed: 0, unluckyMatches: 0, dominatingMatches: 0, luckyWins: 0, outshot: 0, unluckyMatchDetails: [], luckyMatchDetails: [], goalkeeper: null, players18Count: 0 }; }); if (playedMatches.length > 0) { gkLogging.processedTeams.clear(); gkLogging.teamsWithGk.clear(); gkLogging.matchesUsed.clear(); loadingEl.innerHTML = `
Analyzing match data Step 3/3: Processing matches (0/${playedMatches.length})`; const teamIdMap = {}; teams.forEach(team => { teamIdMap[team.id] = team; }); const batchSize = BATCH_SIZE; const maxMatches = playedMatches.length; for (let i = 0; i < maxMatches; i += batchSize) { const batch = playedMatches.slice(i, i + batchSize); const matchPromises = batch.map(match => processMatchComplete(match.mid, teamIdMap)); const matchResults = await Promise.all(matchPromises); if (i < MAX_GK_LOOKUP_ATTEMPTS * batchSize) { const gkPromises = matchResults.filter(Boolean).map(matchData => extractGoalkeeperData(matchData, teamIdMap)); await Promise.all(gkPromises); } matchResults.forEach(matchData => { if (!matchData) return; if (teamStats[matchData.homeTeam.id]) { const homeStats = teamStats[matchData.homeTeam.id]; homeStats.shotsOnTarget += matchData.homeTeam.shotsOnTarget; homeStats.goalsScored += matchData.homeTeam.goals; homeStats.shotsOnTargetReceived += matchData.awayTeam.shotsOnTarget; homeStats.goalsConceded += matchData.awayTeam.goals; homeStats.matchesPlayed++; if (matchData.homeTeam.shotsOnTarget > matchData.awayTeam.shotsOnTarget) { homeStats.dominatingMatches++; if (matchData.homeTeam.result !== 'W') { homeStats.unluckyMatches++; homeStats.unluckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.awayTeam.name, homeTeam: true, result: matchData.homeTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } if (matchData.homeTeam.shotsOnTarget < matchData.awayTeam.shotsOnTarget) { homeStats.outshot++; if (matchData.homeTeam.result === 'W') { homeStats.luckyWins++; homeStats.luckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.awayTeam.name, homeTeam: true, result: matchData.homeTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } } if (teamStats[matchData.awayTeam.id]) { const awayStats = teamStats[matchData.awayTeam.id]; awayStats.shotsOnTarget += matchData.awayTeam.shotsOnTarget; awayStats.goalsScored += matchData.awayTeam.goals; awayStats.shotsOnTargetReceived += matchData.homeTeam.shotsOnTarget; awayStats.goalsConceded += matchData.homeTeam.goals; awayStats.matchesPlayed++; if (matchData.awayTeam.shotsOnTarget > matchData.homeTeam.shotsOnTarget) { awayStats.dominatingMatches++; if (matchData.awayTeam.result !== 'W') { awayStats.unluckyMatches++; awayStats.unluckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.homeTeam.name, homeTeam: false, result: matchData.awayTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } if (matchData.awayTeam.shotsOnTarget < matchData.homeTeam.shotsOnTarget) { awayStats.outshot++; if (matchData.awayTeam.result === 'W') { awayStats.luckyWins++; awayStats.luckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.homeTeam.name, homeTeam: false, result: matchData.awayTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } } }); loadingEl.innerHTML = `
Analyzing match data Step 3/3: Processing matches (${Math.min(i + batch.length, maxMatches)}/${maxMatches}) (Found: ${gkLogging.teamsWithGk.size}/${teams.length} goalkeepers)`; } teams.forEach(team => { if (teamGoalkeeperCache[team.id] && teamStats[team.id]) { teamStats[team.id].goalkeeper = teamGoalkeeperCache[team.id]; log(`Added goalkeeper data for team ${team.name} (${team.id})`, "debug"); } else { log(`No goalkeeper data found for team ${team.name} (${team.id})`, "debug"); } if (teamPlayersCache[team.id] && teamStats[team.id]) { teamStats[team.id].players18Count = teamPlayersCache[team.id].players18Count || 0; log(`Added 18-year-old player count for team ${team.name} (${team.id}): ${teamStats[team.id].players18Count}`, "debug"); } }); log(`Goalkeeper data coverage: ${gkLogging.teamsWithGk.size}/${teams.length} teams (${((gkLogging.teamsWithGk.size / teams.length) * 100).toFixed(1)}%)`, gkLogging.teamsWithGk.size > 0 ? "success" : "warn"); } const validTeams = []; teams.forEach(team => { const stats = teamStats[team.id] || { shotsOnTarget: 0, goalsScored: 0, shotsOnTargetReceived: 0, goalsConceded: 0, matchesPlayed: 0, unluckyMatches: 0, dominatingMatches: 0, luckyWins: 0, outshot: 0, unluckyMatchDetails: [], luckyMatchDetails: [], goalkeeper: null, players18Count: 0 }; if (stats.shotsOnTarget === 0 || stats.shotsOnTargetReceived === 0) { log(`Excluding team ${team.name} (${team.id}) - Has 0 shots on target (for: ${stats.shotsOnTarget}, against: ${stats.shotsOnTargetReceived})`, "warn"); return; } team.shotsOnTarget = stats.shotsOnTarget; team.goalsScored = stats.goalsScored; team.shotsOnTargetReceived = stats.shotsOnTargetReceived; team.goalsConceded = stats.goalsConceded; team.matchesPlayed = stats.matchesPlayed; team.unluckyMatches = stats.unluckyMatches; team.dominatingMatches = stats.dominatingMatches; team.luckyWins = stats.luckyWins; team.outshot = stats.outshot; team.unluckyMatchDetails = stats.unluckyMatchDetails; team.luckyMatchDetails = stats.luckyMatchDetails; team.goalkeeper = stats.goalkeeper; team.players18Count = stats.players18Count; team.offensiveConversionRate = stats.shotsOnTarget > 0 ? (stats.goalsScored / stats.shotsOnTarget * 100).toFixed(1) : '0.0'; team.defensiveConversionRate = stats.shotsOnTargetReceived > 0 ? (stats.goalsConceded / stats.shotsOnTargetReceived * 100).toFixed(1) : '0.0'; let luckIndex = parseFloat(team.offensiveConversionRate) - parseFloat(team.defensiveConversionRate); if (team.goalkeeper && team.goalkeeper.valueInUsd) { const gkValueFactor = Math.min(team.goalkeeper.valueInUsd / 1000000, 5) * 0.5; luckIndex -= gkValueFactor; log(`Team ${team.name} GK adjustment: ${team.goalkeeper.valueInUsd.toFixed(2)} USD = -${gkValueFactor.toFixed(1)} luck points`, "gk"); } else { log(`No goalkeeper data for team ${team.name}`, "warn"); } team.luckIndex = luckIndex.toFixed(1); team.unluckyIndex = stats.dominatingMatches > 0 ? ((stats.unluckyMatches / stats.dominatingMatches) * 100).toFixed(1) : '0.0'; team.luckyWinPct = stats.outshot > 0 ? ((stats.luckyWins / stats.outshot) * 100).toFixed(1) : '0.0'; if (stats.dominatingMatches > 0) { const unluckyPct = stats.unluckyMatches / stats.dominatingMatches; const sampleWeight = 1 + Math.log(Math.max(stats.dominatingMatches, 1)) / 10; team.weightedUnluckyScore = (unluckyPct * sampleWeight * 100).toFixed(1); } else { team.weightedUnluckyScore = '0.0'; } if (stats.outshot > 0) { const luckyPct = stats.luckyWins / stats.outshot; const sampleWeight = 1 + Math.log(Math.max(stats.outshot, 1)) / 10; team.weightedLuckyScore = (luckyPct * sampleWeight * 100).toFixed(1); } else { team.weightedLuckyScore = '0.0'; } validTeams.push(team); }); log(`Filtered teams: ${validTeams.length} of ${teams.length} teams have valid shot data`, "info"); const statLeaders = { bestOffensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.offensiveConversionRate) - parseFloat(a.offensiveConversionRate)).slice(0, 3), worstOffensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.offensiveConversionRate) - parseFloat(b.offensiveConversionRate)).slice(0, 3), bestDefensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.defensiveConversionRate) - parseFloat(b.defensiveConversionRate)).slice(0, 3), worstDefensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.defensiveConversionRate) - parseFloat(a.defensiveConversionRate)).slice(0, 3), luckiest: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.luckIndex) - parseFloat(a.luckIndex)).slice(0, 3), unluckiest: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.luckIndex) - parseFloat(b.luckIndex)).slice(0, 3), mostUnluckyMatches: validTeams.filter(team => team.dominatingMatches > 0).sort((a, b) => parseFloat(b.weightedUnluckyScore) - parseFloat(a.weightedUnluckyScore)).slice(0, 3), mostLuckyWins: validTeams.filter(team => team.outshot > 0).sort((a, b) => parseFloat(b.weightedLuckyScore) - parseFloat(a.weightedLuckyScore)).slice(0, 3) }; displayGkDebugInfo(); if (allLeaguesData !== null) { allLeaguesData[leagueId] = { teams: validTeams, statLeaders, leagueType }; updateGlobalLeaders(allLeaguesData); updateLeagueSelector(allLeaguesData, resultsDiv, leagueId); } else { displayLeagueData(validTeams, statLeaders, leagueId, leagueType, resultsDiv); } log(`League ${leagueId} processed successfully`, "success"); } catch (error) { log(`Error processing league ${leagueId}: ${error.message}`, "error"); if (resultsDiv) { resultsDiv.innerHTML = `

Error: Failed to process league data: ${error.message}

`; showBackButton(resultsDiv); } } } function updateGlobalLeaders(allLeaguesData) { const allTeams = []; for (const leagueId in allLeaguesData) { if (leagueId === 'globalLeaders') continue; allLeaguesData[leagueId].teams.forEach(team => { team.leagueId = leagueId; team.leagueType = allLeaguesData[leagueId].leagueType; allTeams.push(team); }); } const globalLeaders = { bestOffensiveConversion: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.offensiveConversionRate) - parseFloat(a.offensiveConversionRate)).slice(0, 5), worstOffensiveConversion: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.offensiveConversionRate) - parseFloat(b.offensiveConversionRate)).slice(0, 5), bestDefensiveConversion: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.defensiveConversionRate) - parseFloat(b.defensiveConversionRate)).slice(0, 5), worstDefensiveConversion: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.defensiveConversionRate) - parseFloat(a.defensiveConversionRate)).slice(0, 5), luckiest: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.luckIndex) - parseFloat(a.luckIndex)).slice(0, 5), unluckiest: allTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.luckIndex) - parseFloat(b.luckIndex)).slice(0, 5), mostUnluckyMatches: allTeams.filter(team => team.dominatingMatches > 0).sort((a, b) => parseFloat(b.weightedUnluckyScore) - parseFloat(a.weightedUnluckyScore)).slice(0, 5), mostLuckyWins: allTeams.filter(team => team.outshot > 0).sort((a, b) => parseFloat(b.weightedLuckyScore) - parseFloat(a.weightedLuckyScore)).slice(0, 5) }; allLeaguesData.globalLeaders = globalLeaders; log("Global leaders calculated", "success"); } function displayLeagueData(teams, statLeaders, leagueId, leagueType, resultsDiv) { const contentDiv = document.createElement('div'); const tabContainer = document.createElement('div'); tabContainer.className = 'tab-container'; const summaryTab = document.createElement('button'); summaryTab.className = 'tab active'; summaryTab.textContent = 'Statistics Summary'; summaryTab.addEventListener('click', () => activateTab('summary')); const teamsTab = document.createElement('button'); teamsTab.className = 'tab'; teamsTab.textContent = 'All Teams'; teamsTab.addEventListener('click', () => activateTab('teams')); tabContainer.appendChild(summaryTab); tabContainer.appendChild(teamsTab); contentDiv.appendChild(tabContainer); const summaryContent = document.createElement('div'); summaryContent.id = 'summary-tab'; summaryContent.className = 'tab-content active'; const teamsContent = document.createElement('div'); teamsContent.id = 'teams-tab'; teamsContent.className = 'tab-content'; const leagueUrl = `https://www.managerzone.com/?p=league&type=${leagueType}&sid=${leagueId}`; const divisionName = getDivisionDisplayName(leagueId, leagueType); const headerInfo = `

League: ${divisionName} (ID: ${leagueId})

League Type: ${leagueType}

Teams Found: ${teams.length}

GK Data Coverage: ${teams.filter(t => t.goalkeeper).length}/${teams.length}

`; summaryContent.innerHTML = headerInfo; teamsContent.innerHTML = headerInfo; const offensiveCard = createStatCard('Best Conversion Rate', statLeaders.bestOffensiveConversion, team => `${team.name}: ${team.offensiveConversionRate}% (${team.goalsScored}/${team.shotsOnTarget} SoT)${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`, true); const worstOffensiveCard = createStatCard('Worst Conversion Rate', statLeaders.worstOffensiveConversion, team => `${team.name}: ${team.offensiveConversionRate}% (${team.goalsScored}/${team.shotsOnTarget} SoT)${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`, true); const defensiveCard = createStatCard('Best GKs', statLeaders.bestDefensiveConversion, team => { let gkInfo = ''; if (team.goalkeeper && team.goalkeeper.valueInUsd) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = ` | GKValue: $${formattedValue}`; } return `${team.name}: ${team.defensiveConversionRate}% (${team.goalsConceded}/${team.shotsOnTargetReceived} SoT Against)${gkInfo}${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`; }, true); const worstDefensiveCard = createStatCard('Worst GKs', statLeaders.worstDefensiveConversion, team => { let gkInfo = ''; if (team.goalkeeper && team.goalkeeper.valueInUsd) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = ` | GK: $${formattedValue}`; } return `${team.name}: ${team.defensiveConversionRate}% (${team.goalsConceded}/${team.shotsOnTargetReceived} SoT Against)${gkInfo}${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`; }, true); const luckiestCard = createStatCard('Luckiest Teams (Overall)', statLeaders.luckiest, team => `${team.name}: +${team.luckIndex}${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`, true); const unluckiestCard = createStatCard('Unluckiest Teams (Overall)', statLeaders.unluckiest, team => `${team.name}: ${team.luckIndex}${team.players18Count ? ` | 18: ${team.players18Count}` : ''}`, true); const unluckyMatchesCard = createStatCard('Teams with Most Unlucky Matches', statLeaders.mostUnluckyMatches, team => `${team.name}: ${team.unluckyIndex}% (${team.unluckyMatches}/${team.dominatingMatches})${team.players18Count ? ` | 18: ${team.players18Count}` : ''}
Weighted Score: ${team.weightedUnluckyScore} View matches`, true); const luckyWinsCard = createStatCard('Teams with Most Lucky Wins', statLeaders.mostLuckyWins, team => `${team.name}: ${team.luckyWinPct}% (${team.luckyWins}/${team.outshot})${team.players18Count ? ` | 18: ${team.players18Count}` : ''}
Weighted Score: ${team.weightedLuckyScore} View matches`, true); summaryContent.appendChild(offensiveCard); summaryContent.appendChild(worstOffensiveCard); summaryContent.appendChild(defensiveCard); summaryContent.appendChild(worstDefensiveCard); summaryContent.appendChild(luckiestCard); summaryContent.appendChild(unluckiestCard); summaryContent.appendChild(unluckyMatchesCard); summaryContent.appendChild(luckyWinsCard); teams.forEach(team => { const teamDiv = document.createElement('div'); teamDiv.className = 'team-stats'; let gkInfo = ''; if (team.goalkeeper) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = `
Goalkeeper: ${team.goalkeeper.name} (Value: USD ${formattedValue})
`; } let players18Info = ''; if (team.players18Count !== undefined) { players18Info = `
Players Aged 18: ${team.players18Count}
`; } teamDiv.innerHTML = `
${team.name}
${gkInfo} ${players18Info}
Position: ${team.position} | Stats: ${team.wins}W-${team.draws}D-${team.losses}L | Points: ${team.points}
Goals: ${team.goalsFor}-${team.goalsAgainst} (${team.goalDiff})
Offensive: SoT ${team.shotsOnTarget || 0} | Goals ${team.goalsScored || 0} | Conversion ${team.offensiveConversionRate}%
Defensive: SoT Against ${team.shotsOnTargetReceived || 0} | Goals Against ${team.goalsConceded || 0} | Conversion Against ${team.defensiveConversionRate}%
Luck Index: ${team.luckIndex} (higher is better)
Dominating Matches: ${team.dominatingMatches} (had more SoT than opponent)
Unlucky Matches: ${team.unluckyIndex}% (${team.unluckyMatches}/${team.dominatingMatches} matches where they dominated but didn't win)
Weighted Score: ${team.weightedUnluckyScore} ${team.unluckyMatches > 0 ? `View matches` : ''}
Lucky Wins: ${team.luckyWinPct}% (${team.luckyWins}/${team.outshot} matches won despite being outshot)
Weighted Score: ${team.weightedLuckyScore} ${team.luckyWins > 0 ? `View matches` : ''}
Last 6 matches: ${(team.last6 || []).map(m => `${m.result}`).join('')}
`; const unluckyMatchesDiv = document.createElement('div'); unluckyMatchesDiv.className = 'match-list'; unluckyMatchesDiv.dataset.teamId = team.id; unluckyMatchesDiv.dataset.matchType = 'unlucky'; if (team.unluckyMatchDetails.length > 0) { team.unluckyMatchDetails.forEach(match => { const matchItem = document.createElement('div'); matchItem.className = 'match-item'; const resultClass = match.result === 'W' ? 'win' : (match.result === 'D' ? 'draw' : 'loss'); const matchInfo = match.homeTeam ? `${team.name} vs ${match.opponent}` : `${match.opponent} vs ${team.name}`; matchItem.innerHTML = ` ${matchInfo} SoT: ${match.shotsOnTarget} ${match.score} (${match.result})`; unluckyMatchesDiv.appendChild(matchItem); }); } else { unluckyMatchesDiv.innerHTML = '

No unlucky matches found.

'; } const luckyMatchesDiv = document.createElement('div'); luckyMatchesDiv.className = 'match-list'; luckyMatchesDiv.dataset.teamId = team.id; luckyMatchesDiv.dataset.matchType = 'lucky'; if (team.luckyMatchDetails.length > 0) { team.luckyMatchDetails.forEach(match => { const matchItem = document.createElement('div'); matchItem.className = 'match-item'; const resultClass = match.result === 'W' ? 'win' : (match.result === 'D' ? 'draw' : 'loss'); const matchInfo = match.homeTeam ? `${team.name} vs ${match.opponent}` : `${match.opponent} vs ${team.name}`; matchItem.innerHTML = ` ${matchInfo} SoT: ${match.shotsOnTarget} ${match.score} (${match.result})`; luckyMatchesDiv.appendChild(matchItem); }); } else { luckyMatchesDiv.innerHTML = '

No lucky matches found.

'; } teamDiv.appendChild(unluckyMatchesDiv); teamDiv.appendChild(luckyMatchesDiv); teamsContent.appendChild(teamDiv); }); contentDiv.appendChild(summaryContent); contentDiv.appendChild(teamsContent); function activateTab(tabName) { const tabs = contentDiv.querySelectorAll('.tab'); const contents = contentDiv.querySelectorAll('.tab-content'); tabs.forEach(tab => tab.classList.remove('active')); contents.forEach(content => content.classList.remove('active')); if (tabName === 'summary') { summaryTab.classList.add('active'); summaryContent.classList.add('active'); } else { teamsTab.classList.add('active'); teamsContent.classList.add('active'); } } resultsDiv.innerHTML = ''; resultsDiv.appendChild(contentDiv); document.querySelectorAll('.toggle-matches').forEach(toggle => { toggle.addEventListener('click', function(e) { const teamId = this.dataset.teamId; const matchType = this.dataset.matchType; const parentStatItem = this.closest('.stat-item') || this.closest('.team-stats'); if (!parentStatItem) return; const matchList = parentStatItem.querySelector(`.match-list[data-team-id="${teamId}"][data-match-type="${matchType}"]`); if (matchList) { if (matchList.style.display === 'block') { matchList.style.display = 'none'; this.textContent = "View matches"; } else { matchList.style.display = 'block'; this.textContent = "Hide matches"; } } }); }); } function createStatCard(title, teams, formatter, showRank = false) { const card = document.createElement('div'); card.className = 'stat-card'; const cardTitle = document.createElement('div'); cardTitle.className = 'stat-card-title'; cardTitle.textContent = title; card.appendChild(cardTitle); teams.forEach((team, index) => { const item = document.createElement('div'); item.className = 'stat-item'; let content = ''; if (showRank) { content += `${index + 1} `; } content += formatter(team); const itemContentDiv = document.createElement('div'); itemContentDiv.innerHTML = content; item.appendChild(itemContentDiv); if (team.leagueId && team.leagueType) { const divisionName = getDivisionDisplayName(team.leagueId, team.leagueType); const leagueUrl = `https://www.managerzone.com/?p=league&type=${team.leagueType}&sid=${team.leagueId}`; const leagueInfoDiv = document.createElement('div'); leagueInfoDiv.className = 'stat-value'; leagueInfoDiv.innerHTML = `${divisionName}`; item.appendChild(leagueInfoDiv); } card.appendChild(item); if (itemContentDiv.querySelector('.toggle-matches')) { const toggleElement = itemContentDiv.querySelector('.toggle-matches'); const matchType = toggleElement.dataset.matchType; const matchDetails = matchType === 'unlucky' ? team.unluckyMatchDetails : team.luckyMatchDetails; if (matchDetails && matchDetails.length > 0) { const matchesDiv = document.createElement('div'); matchesDiv.className = 'match-list'; matchesDiv.dataset.teamId = team.id; matchesDiv.dataset.matchType = matchType; matchesDiv.style.display = 'none'; matchesDiv.style.width = '100%'; matchDetails.forEach(match => { const matchItem = document.createElement('div'); matchItem.className = 'match-item'; const resultClass = match.result === 'W' ? 'win' : (match.result === 'D' ? 'draw' : 'loss'); const matchInfo = match.homeTeam ? `${team.name} vs ${match.opponent}` : `${match.opponent} vs ${team.name}`; matchItem.innerHTML = ` ${matchInfo} SoT: ${match.shotsOnTarget} ${match.score} (${match.result})`; matchesDiv.appendChild(matchItem); }); item.appendChild(matchesDiv); } } }); return card; } function updateLeagueSelector(allLeaguesData, resultsDiv, currentLeagueId) { resultsDiv.innerHTML = ''; const tabContainer = document.createElement('div'); tabContainer.className = 'tab-container'; const globalTab = document.createElement('button'); globalTab.className = 'tab active'; globalTab.textContent = 'Global Leaders'; const leagueTab = document.createElement('button'); leagueTab.className = 'tab'; leagueTab.textContent = 'League View'; tabContainer.appendChild(globalTab); tabContainer.appendChild(leagueTab); const globalContent = document.createElement('div'); globalContent.id = 'global-content'; globalContent.className = 'tab-content active'; const leagueContent = document.createElement('div'); leagueContent.id = 'league-content'; leagueContent.className = 'tab-content'; const backButtonContainer = document.createElement('div'); const backButton = document.createElement('button'); backButton.className = 'back-button'; backButton.textContent = '← Back to Options'; backButton.style.marginBottom = '15px'; backButton.addEventListener('click', function() { resultsDiv.style.display = 'none'; resultsDiv.innerHTML = ''; document.querySelectorAll('.unluckyOption').forEach(option => { option.style.display = 'block'; }); }); backButtonContainer.appendChild(backButton); const leagueIds = Object.keys(allLeaguesData) .filter(key => key !== 'globalLeaders') .sort((a, b) => parseInt(a) - parseInt(b)); let currentActiveLeagueIndex = leagueIds.indexOf(currentLeagueId); if (currentActiveLeagueIndex === -1 && leagueIds.length > 0) { currentActiveLeagueIndex = 0; currentLeagueId = leagueIds[0]; } if (allLeaguesData.globalLeaders) { const globalLeaders = allLeaguesData.globalLeaders; const globalSubTabContainer = document.createElement('div'); globalSubTabContainer.className = 'tab-container'; const globalSummaryTab = document.createElement('button'); globalSummaryTab.className = 'tab active'; globalSummaryTab.textContent = 'Statistics Summary'; const globalTeamsTab = document.createElement('button'); globalTeamsTab.className = 'tab'; globalTeamsTab.textContent = 'All Teams'; globalSubTabContainer.appendChild(globalSummaryTab); globalSubTabContainer.appendChild(globalTeamsTab); const globalSummaryContent = document.createElement('div'); globalSummaryContent.id = 'global-summary-tab'; globalSummaryContent.className = 'tab-content active'; const globalTeamsContent = document.createElement('div'); globalTeamsContent.id = 'global-teams-tab'; globalTeamsContent.className = 'tab-content'; const globalHeader = document.createElement('div'); globalHeader.innerHTML = `

Global Statistics Leaders

Showing the best and worst teams across all analyzed leagues

`; globalSummaryContent.appendChild(globalHeader.cloneNode(true)); globalTeamsContent.appendChild(globalHeader.cloneNode(true)); const bestOffensive = createStatCard('Best Offensive Conversion (Global)', globalLeaders.bestOffensiveConversion, team => `${team.name}: ${team.offensiveConversionRate}% (${team.goalsScored}/${team.shotsOnTarget} SoT)${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`, true); const worstOffensive = createStatCard('Worst Offensive Conversion (Global)', globalLeaders.worstOffensiveConversion, team => `${team.name}: ${team.offensiveConversionRate}% (${team.goalsScored}/${team.shotsOnTarget} SoT)${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`, true); const bestDefensive = createStatCard('Best GKs (Global)', globalLeaders.bestDefensiveConversion, team => { let gkInfo = ''; if (team.goalkeeper && team.goalkeeper.valueInUsd) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = ` | GK: $${formattedValue}`; } return `${team.name}: ${team.defensiveConversionRate}% (${team.goalsConceded}/${team.shotsOnTargetReceived} SoT Against)${gkInfo}${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`; }, true); const worstDefensive = createStatCard('Worst GKs (Global)', globalLeaders.worstDefensiveConversion, team => { let gkInfo = ''; if (team.goalkeeper && team.goalkeeper.valueInUsd) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = ` | GK: $${formattedValue}`; } return `${team.name}: ${team.defensiveConversionRate}% (${team.goalsConceded}/${team.shotsOnTargetReceived} SoT Against)${gkInfo}${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`; }, true); const luckiest = createStatCard('Luckiest Teams (Global)', globalLeaders.luckiest, team => `${team.name}: +${team.luckIndex}${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`, true); const unluckiest = createStatCard('Unluckiest Teams (Global)', globalLeaders.unluckiest, team => `${team.name}: ${team.luckIndex}${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}`, true); const unluckyMatches = createStatCard('Teams with Most Unlucky Matches (Global)', globalLeaders.mostUnluckyMatches, team => `${team.name}: ${team.unluckyIndex}% (${team.unluckyMatches}/${team.dominatingMatches} matches)${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}
Weighted Score: ${team.weightedUnluckyScore} View matches`, true); const luckyWins = createStatCard('Teams with Most Lucky Wins (Global)', globalLeaders.mostLuckyWins, team => `${team.name}: ${team.luckyWinPct}% (${team.luckyWins}/${team.outshot} matches)${team.players18Count ? ` | Players 18: ${team.players18Count}` : ''}
Weighted Score: ${team.weightedLuckyScore} View matches`, true); globalSummaryContent.appendChild(bestOffensive); globalSummaryContent.appendChild(worstOffensive); globalSummaryContent.appendChild(bestDefensive); globalSummaryContent.appendChild(worstDefensive); globalSummaryContent.appendChild(luckiest); globalSummaryContent.appendChild(unluckiest); globalSummaryContent.appendChild(unluckyMatches); globalSummaryContent.appendChild(luckyWins); const globalTeamsListContainer = document.createElement('div'); const allTeams = []; for (const leagueId in allLeaguesData) { if (leagueId === 'globalLeaders') continue; allLeaguesData[leagueId].teams.forEach(team => { team.leagueId = leagueId; team.leagueType = allLeaguesData[leagueId].leagueType; allTeams.push(team); }); } const sortedTeams = allTeams.sort((a, b) => parseFloat(a.luckIndex) - parseFloat(b.luckIndex)); sortedTeams.forEach(team => { const teamDiv = document.createElement('div'); teamDiv.className = 'team-stats'; let gkInfo = ''; if (team.goalkeeper) { let formattedValue; const valueInUsd = team.goalkeeper.valueInUsd; if (valueInUsd >= 1000000) { formattedValue = `${(valueInUsd / 1000000).toFixed(2)}M`; } else { formattedValue = `${(valueInUsd / 1000).toFixed(0)}k`; } gkInfo = `
Goalkeeper: ${team.goalkeeper.name} (Value: USD ${formattedValue})
`; } const divisionName = getDivisionDisplayName(team.leagueId, team.leagueType); const leagueUrl = `https://www.managerzone.com/?p=league&type=${team.leagueType}&sid=${team.leagueId}`; teamDiv.innerHTML = `
${team.name} ${divisionName}
${gkInfo} ${team.players18Count ? `
Players Aged 18: ${team.players18Count}
` : ''}
Position: ${team.position} | Stats: ${team.wins}W-${team.draws}D-${team.losses}L | Points: ${team.points}
Goals: ${team.goalsFor}-${team.goalsAgainst} (${team.goalDiff})
Offensive: SoT ${team.shotsOnTarget || 0} | Goals ${team.goalsScored || 0} | Conversion ${team.offensiveConversionRate}%
Defensive: SoT Against ${team.shotsOnTargetReceived || 0} | Goals Against ${team.goalsConceded || 0} | Conversion Against ${team.defensiveConversionRate}%
Luck Index: ${team.luckIndex} (higher is better)
Dominating Matches: ${team.dominatingMatches} (had more SoT than opponent)
Unlucky Matches: ${team.unluckyIndex}% (${team.unluckyMatches}/${team.dominatingMatches} matches where they dominated but didn't win)
Weighted Score: ${team.weightedUnluckyScore} ${team.unluckyMatches > 0 ? `View matches` : ''}
Lucky Wins: ${team.luckyWinPct}% (${team.luckyWins}/${team.outshot} matches won despite being outshot)
Weighted Score: ${team.weightedLuckyScore} ${team.luckyWins > 0 ? `View matches` : ''}
`; const unluckyMatchesDiv = document.createElement('div'); unluckyMatchesDiv.className = 'match-list'; unluckyMatchesDiv.dataset.teamId = team.id; unluckyMatchesDiv.dataset.matchType = 'unlucky'; unluckyMatchesDiv.style.display = 'none'; if (team.unluckyMatchDetails && team.unluckyMatchDetails.length > 0) { team.unluckyMatchDetails.forEach(match => { const matchItem = document.createElement('div'); matchItem.className = 'match-item'; const resultClass = match.result === 'W' ? 'win' : (match.result === 'D' ? 'draw' : 'loss'); const matchInfo = match.homeTeam ? `${team.name} vs ${match.opponent}` : `${match.opponent} vs ${team.name}`; matchItem.innerHTML = ` ${matchInfo} SoT: ${match.shotsOnTarget} ${match.score} (${match.result})`; unluckyMatchesDiv.appendChild(matchItem); }); } else { unluckyMatchesDiv.innerHTML = '

No unlucky matches found.

'; } const luckyMatchesDiv = document.createElement('div'); luckyMatchesDiv.className = 'match-list'; luckyMatchesDiv.dataset.teamId = team.id; luckyMatchesDiv.dataset.matchType = 'lucky'; luckyMatchesDiv.style.display = 'none'; if (team.luckyMatchDetails && team.luckyMatchDetails.length > 0) { team.luckyMatchDetails.forEach(match => { const matchItem = document.createElement('div'); matchItem.className = 'match-item'; const resultClass = match.result === 'W' ? 'win' : (match.result === 'D' ? 'draw' : 'loss'); const matchInfo = match.homeTeam ? `${team.name} vs ${match.opponent}` : `${match.opponent} vs ${team.name}`; matchItem.innerHTML = ` ${matchInfo} SoT: ${match.shotsOnTarget} ${match.score} (${match.result})`; luckyMatchesDiv.appendChild(matchItem); }); } else { luckyMatchesDiv.innerHTML = '

No lucky matches found.

'; } teamDiv.appendChild(unluckyMatchesDiv); teamDiv.appendChild(luckyMatchesDiv); globalTeamsListContainer.appendChild(teamDiv); }); globalTeamsContent.appendChild(globalTeamsListContainer); globalSummaryTab.addEventListener('click', () => { globalSummaryTab.classList.add('active'); globalTeamsTab.classList.remove('active'); globalSummaryContent.classList.add('active'); globalTeamsContent.classList.remove('active'); }); globalTeamsTab.addEventListener('click', () => { globalSummaryTab.classList.remove('active'); globalTeamsTab.classList.add('active'); globalSummaryContent.classList.remove('active'); globalTeamsContent.classList.add('active'); }); globalContent.appendChild(globalSubTabContainer); globalContent.appendChild(globalSummaryContent); globalContent.appendChild(globalTeamsContent); } const selectorContainer = document.createElement('div'); selectorContainer.className = 'league-selector'; const selectorTitle = document.createElement('div'); selectorTitle.className = 'league-selector-title'; selectorTitle.textContent = 'Select a League:'; selectorContainer.appendChild(selectorTitle); const leagueGrid = document.createElement('div'); leagueGrid.className = 'league-grid'; leagueIds.forEach((leagueId, index) => { const leagueButton = document.createElement('div'); leagueButton.className = 'league-item'; if (index === currentActiveLeagueIndex) { leagueButton.classList.add('active'); } const leagueType = allLeaguesData[leagueId].leagueType; const divisionName = getDivisionDisplayName(leagueId, leagueType); const leagueUrl = `https://www.managerzone.com/?p=league&type=${leagueType}&sid=${leagueId}`; leagueButton.innerHTML = `${divisionName}`; leagueButton.dataset.leagueId = leagueId; leagueButton.addEventListener('click', (e) => { if (e.target.closest('.league-link')) { return; } e.preventDefault(); const clickedLeagueId = e.currentTarget.dataset.leagueId; displayLeagueContent(clickedLeagueId); }); leagueGrid.appendChild(leagueButton); }); selectorContainer.appendChild(leagueGrid); const paginationContainer = document.createElement('div'); paginationContainer.className = 'pagination'; paginationContainer.style.marginTop = '15px'; if (leagueIds.length > 1) { const prevButton = document.createElement('button'); prevButton.className = 'pagination-button prev'; prevButton.innerHTML = '« Previous League'; prevButton.disabled = currentActiveLeagueIndex <= 0; prevButton.style.opacity = prevButton.disabled ? '0.5' : '1'; prevButton.addEventListener('click', () => { if (currentActiveLeagueIndex > 0) { displayLeagueContent(leagueIds[currentActiveLeagueIndex - 1]); } }); const nextButton = document.createElement('button'); nextButton.className = 'pagination-button next'; nextButton.innerHTML = 'Next League »'; nextButton.disabled = currentActiveLeagueIndex >= leagueIds.length - 1; nextButton.style.opacity = nextButton.disabled ? '0.5' : '1'; nextButton.addEventListener('click', () => { if (currentActiveLeagueIndex < leagueIds.length - 1) { displayLeagueContent(leagueIds[currentActiveLeagueIndex + 1]); } }); paginationContainer.appendChild(prevButton); paginationContainer.appendChild(nextButton); selectorContainer.appendChild(paginationContainer); } leagueContent.appendChild(selectorContainer); function displayLeagueContent(leagueId) { const newLeagueIndex = leagueIds.indexOf(leagueId); if (newLeagueIndex === -1) return; const leagueData = allLeaguesData[leagueId]; currentActiveLeagueIndex = newLeagueIndex; document.querySelectorAll('.league-item').forEach(item => { item.classList.remove('active'); if (item.dataset.leagueId === leagueId) { item.classList.add('active'); } }); const prevButton = paginationContainer?.querySelector('.pagination-button.prev'); const nextButton = paginationContainer?.querySelector('.pagination-button.next'); if (prevButton) { prevButton.disabled = newLeagueIndex <= 0; prevButton.style.opacity = prevButton.disabled ? '0.5' : '1'; } if (nextButton) { nextButton.disabled = newLeagueIndex >= leagueIds.length - 1; nextButton.style.opacity = nextButton.disabled ? '0.5' : '1'; } const leagueDataContainer = document.createElement('div'); leagueDataContainer.id = 'league-data-container'; displayLeagueData(leagueData.teams, leagueData.statLeaders, leagueId, leagueData.leagueType, leagueDataContainer); const existingContainer = leagueContent.querySelector('#league-data-container'); if (existingContainer) { leagueContent.replaceChild(leagueDataContainer, existingContainer); } else { leagueContent.appendChild(leagueDataContainer); } leagueDataContainer.querySelectorAll('.toggle-matches').forEach(toggle => { toggle.addEventListener('click', function(e) { const teamId = this.dataset.teamId; const matchType = this.dataset.matchType; const parentStatItem = this.closest('.stat-item') || this.closest('.team-stats'); if (!parentStatItem) return; const matchList = parentStatItem.querySelector(`.match-list[data-team-id="${teamId}"][data-match-type="${matchType}"]`); if (matchList) { if (matchList.style.display === 'block') { matchList.style.display = 'none'; this.textContent = "View matches"; } else { matchList.style.display = 'block'; this.textContent = "Hide matches"; } } }); }); globalTab.classList.remove('active'); leagueTab.classList.add('active'); globalContent.classList.remove('active'); leagueContent.classList.add('active'); resultsDiv.scrollTop = 0; } const initialLeagueDataContainer = document.createElement('div'); initialLeagueDataContainer.id = 'league-data-container'; leagueContent.appendChild(initialLeagueDataContainer); if (currentActiveLeagueIndex !== -1) { displayLeagueContent(leagueIds[currentActiveLeagueIndex]); } globalTab.addEventListener('click', () => { globalTab.classList.add('active'); leagueTab.classList.remove('active'); globalContent.classList.add('active'); leagueContent.classList.remove('active'); }); leagueTab.addEventListener('click', () => { globalTab.classList.remove('active'); leagueTab.classList.add('active'); globalContent.classList.remove('active'); leagueContent.classList.add('active'); }); const mainContent = document.createElement('div'); mainContent.appendChild(backButtonContainer); mainContent.appendChild(tabContainer); mainContent.appendChild(globalContent); mainContent.appendChild(leagueContent); resultsDiv.appendChild(mainContent); globalContent.querySelectorAll('.toggle-matches').forEach(toggle => { toggle.addEventListener('click', function(e) { const teamId = this.dataset.teamId; const matchType = this.dataset.matchType; const parentStatItem = this.closest('.stat-item') || this.closest('.team-stats'); if (!parentStatItem) return; const matchList = parentStatItem.querySelector(`.match-list[data-team-id="${teamId}"][data-match-type="${matchType}"]`); if (matchList) { if (matchList.style.display === 'block') { matchList.style.display = 'none'; this.textContent = "View matches"; } else { matchList.style.display = 'block'; this.textContent = "Hide matches"; } } }); }); } async function processMultipleLeagues(leagueEntries, resultsDiv) { const allLeaguesData = {}; const loadingEl = document.createElement('div'); loadingEl.style.display = 'flex'; loadingEl.style.justifyContent = 'center'; loadingEl.style.alignItems = 'center'; loadingEl.style.margin = '30px 0'; loadingEl.innerHTML = `
Processing leagues (0/${leagueEntries.length})...`; resultsDiv.innerHTML = ''; resultsDiv.appendChild(loadingEl); for (let i = 0; i < leagueEntries.length; i++) { const entry = leagueEntries[i]; const leagueId = entry.id; const leagueType = entry.type; const divisionName = getDivisionDisplayName(leagueId, leagueType); loadingEl.innerHTML = `
Processing ${divisionName} (${i+1}/${leagueEntries.length}) - Type: ${leagueType} Step 1/3: Fetching info`; try { const tableUrl = `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${leagueId}&tid=1&sport=soccer&sub=table`; const scheduleUrl = `https://www.managerzone.com/ajax.php?p=league&type=${leagueType}&sid=${leagueId}&tid=1&sport=soccer&sub=schedule`; log(`Processing league ID: ${leagueId} (Type: ${leagueType})`, "info"); const [tableHTML, scheduleHTML] = await Promise.all([makeRequest(tableUrl), makeRequest(scheduleUrl)]); loadingEl.innerHTML = `
Processing ${divisionName} (${i+1}/${leagueEntries.length}) - Type: ${leagueType} Step 2/3: Processing teams`; const teams = extractTeamData(tableHTML); if (teams.length > 0) { const allMatches = extractScheduleData(scheduleHTML, teams); const playedMatches = allMatches.filter(match => /^\d+\s*-\s*\d+$/.test(match.result)); log(`Found ${teams.length} teams and ${playedMatches.length} played matches in league ${leagueId}`, "info"); const teamStats = {}; teams.forEach(team => { teamStats[team.id] = { name: team.name, shotsOnTarget: 0, goalsScored: 0, shotsOnTargetReceived: 0, goalsConceded: 0, matchesPlayed: 0, unluckyMatches: 0, dominatingMatches: 0, luckyWins: 0, outshot: 0, unluckyMatchDetails: [], luckyMatchDetails: [], goalkeeper: null, players18Count: 0 }; }); if (playedMatches.length > 0) { loadingEl.innerHTML = `
Processing ${divisionName} (${i+1}/${leagueEntries.length}) - Type: ${leagueType} Step 3/3: Analyzing matches`; const teamIdMap = {}; teams.forEach(team => { teamIdMap[team.id] = team; }); gkLogging.processedTeams.clear(); gkLogging.teamsWithGk.clear(); gkLogging.matchesUsed.clear(); const batchSize = BATCH_SIZE; const maxMatches = playedMatches.length; for (let j = 0; j < maxMatches; j += batchSize) { const batch = playedMatches.slice(j, j + batchSize); const matchPromises = batch.map(match => processMatchComplete(match.mid, teamIdMap)); const matchResults = await Promise.all(matchPromises); if (j < MAX_GK_LOOKUP_ATTEMPTS * batchSize) { const gkPromises = matchResults.filter(Boolean).map(matchData => extractGoalkeeperData(matchData, teamIdMap)); await Promise.all(gkPromises); } matchResults.forEach(matchData => { if (!matchData) return; if (teamStats[matchData.homeTeam.id]) { const homeStats = teamStats[matchData.homeTeam.id]; homeStats.shotsOnTarget += matchData.homeTeam.shotsOnTarget; homeStats.goalsScored += matchData.homeTeam.goals; homeStats.shotsOnTargetReceived += matchData.awayTeam.shotsOnTarget; homeStats.goalsConceded += matchData.awayTeam.goals; homeStats.matchesPlayed++; if (matchData.homeTeam.shotsOnTarget > matchData.awayTeam.shotsOnTarget) { homeStats.dominatingMatches++; if (matchData.homeTeam.result !== 'W') { homeStats.unluckyMatches++; homeStats.unluckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.awayTeam.name, homeTeam: true, result: matchData.homeTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } if (matchData.homeTeam.shotsOnTarget < matchData.awayTeam.shotsOnTarget) { homeStats.outshot++; if (matchData.homeTeam.result === 'W') { homeStats.luckyWins++; homeStats.luckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.awayTeam.name, homeTeam: true, result: matchData.homeTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } } if (teamStats[matchData.awayTeam.id]) { const awayStats = teamStats[matchData.awayTeam.id]; awayStats.shotsOnTarget += matchData.awayTeam.shotsOnTarget; awayStats.goalsScored += matchData.awayTeam.goals; awayStats.shotsOnTargetReceived += matchData.homeTeam.shotsOnTarget; awayStats.goalsConceded += matchData.homeTeam.goals; awayStats.matchesPlayed++; if (matchData.awayTeam.shotsOnTarget > matchData.homeTeam.shotsOnTarget) { awayStats.dominatingMatches++; if (matchData.awayTeam.result !== 'W') { awayStats.unluckyMatches++; awayStats.unluckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.homeTeam.name, homeTeam: false, result: matchData.awayTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } if (matchData.awayTeam.shotsOnTarget < matchData.homeTeam.shotsOnTarget) { awayStats.outshot++; if (matchData.awayTeam.result === 'W') { awayStats.luckyWins++; awayStats.luckyMatchDetails.push({ mid: matchData.mid, opponent: matchData.homeTeam.name, homeTeam: false, result: matchData.awayTeam.result, score: `${matchData.homeTeam.goals}-${matchData.awayTeam.goals}`, shotsOnTarget: `${matchData.homeTeam.shotsOnTarget}-${matchData.awayTeam.shotsOnTarget}` }); } } } }); loadingEl.innerHTML = `
Processing ${divisionName} (${i+1}/${leagueEntries.length}) - Type: ${leagueType} Step 3/3: Analyzing matches (${Math.min(j + batch.length, maxMatches)}/${maxMatches} processed) (Found: ${gkLogging.teamsWithGk.size}/${teams.length} GKs)`; } } teams.forEach(team => { if (teamGoalkeeperCache[team.id] && teamStats[team.id]) { teamStats[team.id].goalkeeper = teamGoalkeeperCache[team.id]; } if (teamPlayersCache[team.id] && teamStats[team.id]) { teamStats[team.id].players18Count = teamPlayersCache[team.id].players18Count || 0; } }); const validTeams = []; teams.forEach(team => { const stats = teamStats[team.id] || { shotsOnTarget: 0, goalsScored: 0, shotsOnTargetReceived: 0, goalsConceded: 0, matchesPlayed: 0, unluckyMatches: 0, dominatingMatches: 0, luckyWins: 0, outshot: 0, unluckyMatchDetails: [], luckyMatchDetails: [], goalkeeper: null, players18Count: 0 }; if (stats.shotsOnTarget === 0 || stats.shotsOnTargetReceived === 0) { log(`Excluding team ${team.name} (${team.id}) - Has 0 shots on target (for: ${stats.shotsOnTarget}, against: ${stats.shotsOnTargetReceived})`, "warn"); return; } team.shotsOnTarget = stats.shotsOnTarget; team.goalsScored = stats.goalsScored; team.shotsOnTargetReceived = stats.shotsOnTargetReceived; team.goalsConceded = stats.goalsConceded; team.matchesPlayed = stats.matchesPlayed; team.unluckyMatches = stats.unluckyMatches; team.dominatingMatches = stats.dominatingMatches; team.luckyWins = stats.luckyWins; team.outshot = stats.outshot; team.unluckyMatchDetails = stats.unluckyMatchDetails; team.luckyMatchDetails = stats.luckyMatchDetails; team.goalkeeper = stats.goalkeeper; team.players18Count = stats.players18Count; team.offensiveConversionRate = stats.shotsOnTarget > 0 ? (stats.goalsScored / stats.shotsOnTarget * 100).toFixed(1) : '0.0'; team.defensiveConversionRate = stats.shotsOnTargetReceived > 0 ? (stats.goalsConceded / stats.shotsOnTargetReceived * 100).toFixed(1) : '0.0'; let luckIndex = parseFloat(team.offensiveConversionRate) - parseFloat(team.defensiveConversionRate); if (team.goalkeeper && team.goalkeeper.valueInUsd) { const gkValueFactor = Math.min(team.goalkeeper.valueInUsd / 1000000, 5) * 0.5; luckIndex -= gkValueFactor; log(`Team ${team.name} GK adjustment: ${team.goalkeeper.valueInUsd.toFixed(2)} USD = -${gkValueFactor.toFixed(1)} luck points`, "gk"); } team.luckIndex = luckIndex.toFixed(1); team.unluckyIndex = stats.dominatingMatches > 0 ? ((stats.unluckyMatches / stats.dominatingMatches) * 100).toFixed(1) : '0.0'; team.luckyWinPct = stats.outshot > 0 ? ((stats.luckyWins / stats.outshot) * 100).toFixed(1) : '0.0'; if (stats.dominatingMatches > 0) { const unluckyPct = stats.unluckyMatches / stats.dominatingMatches; const sampleWeight = 1 + Math.log(Math.max(stats.dominatingMatches, 1)) / 10; team.weightedUnluckyScore = (unluckyPct * sampleWeight * 100).toFixed(1); } else { team.weightedUnluckyScore = '0.0'; } if (stats.outshot > 0) { const luckyPct = stats.luckyWins / stats.outshot; const sampleWeight = 1 + Math.log(Math.max(stats.outshot, 1)) / 10; team.weightedLuckyScore = (luckyPct * sampleWeight * 100).toFixed(1); } else { team.weightedLuckyScore = '0.0'; } validTeams.push(team); }); log(`Filtered teams: ${validTeams.length} of ${teams.length} teams have valid shot data`, "info"); const statLeaders = { bestOffensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.offensiveConversionRate) - parseFloat(a.offensiveConversionRate)).slice(0, 3), worstOffensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.offensiveConversionRate) - parseFloat(b.offensiveConversionRate)).slice(0, 3), bestDefensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.defensiveConversionRate) - parseFloat(b.defensiveConversionRate)).slice(0, 3), worstDefensiveConversion: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.defensiveConversionRate) - parseFloat(a.defensiveConversionRate)).slice(0, 3), luckiest: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(b.luckIndex) - parseFloat(a.luckIndex)).slice(0, 3), unluckiest: validTeams.filter(team => team.matchesPlayed > 0).sort((a, b) => parseFloat(a.luckIndex) - parseFloat(b.luckIndex)).slice(0, 3), mostUnluckyMatches: validTeams.filter(team => team.dominatingMatches > 0).sort((a, b) => parseFloat(b.weightedUnluckyScore) - parseFloat(a.weightedUnluckyScore)).slice(0, 3), mostLuckyWins: validTeams.filter(team => team.outshot > 0).sort((a, b) => parseFloat(b.weightedLuckyScore) - parseFloat(a.weightedLuckyScore)).slice(0, 3) }; if (playedMatches.length > 0 && validTeams.length > 0) { allLeaguesData[leagueId] = { teams: validTeams, statLeaders, leagueType }; } } } catch (error) { log(`Error processing league ${leagueId} (Type: ${leagueType}): ${error.message}`, "error"); continue; } loadingEl.innerHTML = `
Processed ${divisionName} (${i+1}/${leagueEntries.length})`; } loadingEl.innerHTML = `
Finalizing global statistics...`; displayGkDebugInfo(); updateGlobalLeaders(allLeaguesData); const leagueIds = Object.keys(allLeaguesData) .filter(key => key !== 'globalLeaders') .sort((a, b) => parseInt(a) - parseInt(b)); if (leagueIds.length > 0) { updateLeagueSelector(allLeaguesData, resultsDiv, leagueIds[0]); } else { resultsDiv.innerHTML = '

No leagues with valid match data found in the specified leagues.

'; showBackButton(resultsDiv); } } function showLeagueInputForm(resultsDiv) { const formDiv = document.createElement('div'); formDiv.style.marginBottom = '20px'; const header = document.createElement('p'); header.textContent = 'Add leagues (by ID & Type) to analyze:'; formDiv.appendChild(header); const leagueEntryContainer = document.createElement('div'); leagueEntryContainer.className = 'league-entry-container'; const entryForm = document.createElement('div'); entryForm.className = 'league-entry-form'; const sidInput = document.createElement('input'); sidInput.type = 'text'; sidInput.placeholder = 'League IDs (e.g., 1, 2, 3)'; sidInput.autocomplete = 'off'; const typeSelect = document.createElement('select'); LEAGUE_TYPES.forEach(lt => { const option = document.createElement('option'); option.value = lt.value; option.textContent = lt.label; typeSelect.appendChild(option); }); const addButton = document.createElement('button'); addButton.className = 'add-button'; addButton.innerHTML = '+'; addButton.title = 'Add Leagues'; entryForm.appendChild(sidInput); entryForm.appendChild(typeSelect); entryForm.appendChild(addButton); const leagueList = document.createElement('div'); leagueList.className = 'league-list'; leagueList.style.display = 'none'; const leagues = []; const updateSubmitButton = () => { submitButton.disabled = leagues.length === 0; submitButton.style.opacity = submitButton.disabled ? 0.6 : 1; submitButton.style.cursor = submitButton.disabled ? 'not-allowed' : 'pointer'; }; function updateLeagueList() { if (leagues.length === 0) { leagueList.style.display = 'none'; updateSubmitButton(); return; } leagueList.style.display = 'block'; leagueList.innerHTML = ''; leagues.forEach((league, index) => { const leagueItem = document.createElement('div'); leagueItem.className = 'league-list-item'; const divisionName = getDivisionDisplayName(league.id, league.type); const typeLabel = LEAGUE_TYPES.find(lt => lt.value === league.type)?.label || league.type; leagueItem.innerHTML = `
${divisionName} (${typeLabel})
`; leagueList.appendChild(leagueItem); }); leagueList.querySelectorAll('.remove-button').forEach(button => { button.addEventListener('click', function() { const index = parseInt(this.dataset.index); leagues.splice(index, 1); updateLeagueList(); }); }); updateSubmitButton(); } function addLeague() { const rawIds = sidInput.value.trim(); const selectedType = typeSelect.value; if (!rawIds) { alert('Please enter at least one league ID.'); return; } const potentialIds = rawIds.split(/[\s,]+/).filter(id => id); let addedCount = 0; let invalidCount = 0; let duplicateCount = 0; potentialIds.forEach(idString => { const potentialId = idString.trim(); if (potentialId && /^\d+$/.test(potentialId)) { if (leagues.some(l => l.id === potentialId && l.type === selectedType)) { duplicateCount++; } else { leagues.push({ id: potentialId, type: selectedType }); addedCount++; } } else if (potentialId) { invalidCount++; } }); if (addedCount > 0) { updateLeagueList(); } sidInput.value = ''; sidInput.focus(); let feedback = []; if (addedCount > 0) feedback.push(`Added ${addedCount} league(s).`); if (invalidCount > 0) feedback.push(`Ignored ${invalidCount} invalid entries.`); if (duplicateCount > 0) feedback.push(`Skipped ${duplicateCount} duplicate entries.`); if (feedback.length > 0 && (invalidCount > 0 || duplicateCount > 0)) { setTimeout(() => alert(feedback.join(' ')), 100); } else if (addedCount === 0 && invalidCount === 0 && duplicateCount === 0) { alert('No valid, new league IDs were entered.'); } } addButton.addEventListener('click', addLeague); sidInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') addLeague(); }); const submitButton = document.createElement('button'); submitButton.id = 'leagues-submit'; submitButton.className = 'unluckyOption'; submitButton.style.marginTop = '15px'; submitButton.textContent = 'Analyze Leagues'; submitButton.disabled = true; updateSubmitButton(); submitButton.addEventListener('click', () => { if (leagues.length > 0) { const summaryText = leagues.map(league => { const divName = getDivisionDisplayName(league.id, league.type); const typeLabel = LEAGUE_TYPES.find(lt => lt.value === league.type)?.label || league.type; return `${divName} (${typeLabel})`; }).join(', '); resultsDiv.innerHTML = `

Processing ${leagues.length} leagues...

`; processMultipleLeagues(leagues, resultsDiv); } }); leagueEntryContainer.appendChild(entryForm); leagueEntryContainer.appendChild(leagueList); formDiv.appendChild(leagueEntryContainer); formDiv.appendChild(submitButton); resultsDiv.innerHTML = ''; resultsDiv.appendChild(formDiv); showBackButton(resultsDiv); setTimeout(() => sidInput.focus(), 100); } function showSidPrompt(resultsDiv, isSpecificOption = false) { if (isSpecificOption) { const currentLeagueId = getCurrentLeagueId(); const currentLeagueType = getCurrentLeagueType(); const formDiv = document.createElement('div'); formDiv.style.marginBottom = '20px'; formDiv.innerHTML = `

Please enter a league ID and select type:

`; resultsDiv.innerHTML = ''; resultsDiv.appendChild(formDiv); const sidInput = document.getElementById('sid-input'); const typeSelect = document.getElementById('type-select'); const sidSubmit = document.getElementById('sid-submit'); const handleSubmit = () => { const sid = sidInput.value.trim(); const type = typeSelect.value; if (sid && /^\d+$/.test(sid)) { const divisionName = getDivisionDisplayName(sid, type); resultsDiv.innerHTML = `

Loading data for ${divisionName} (Type: ${type})...

`; processLeagueData(sid, resultsDiv, null, type); } else { let errorMsg = 'Please enter a valid league ID (numbers only).'; formDiv.insertAdjacentHTML('afterbegin', `

${errorMsg}

`); } }; sidSubmit.addEventListener('click', handleSubmit); sidInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSubmit(); }); showBackButton(resultsDiv); setTimeout(() => sidInput.focus(), 100); } else { const leagueId = getCurrentLeagueId(); const leagueType = getCurrentLeagueType(); if (!leagueId || !leagueType) { resultsDiv.innerHTML = `

Error: Could not detect league ID or type from URL.

`; showBackButton(resultsDiv); return; } const divisionName = getDivisionDisplayName(leagueId, leagueType); resultsDiv.innerHTML = `

Loading data for ${divisionName} (Type: ${leagueType})...

`; processLeagueData(leagueId, resultsDiv, null, leagueType); } } function findUnluckyTeams(scope) { document.querySelectorAll('.unluckyOption').forEach(option => { option.style.display = 'none'; }); const resultsDiv = document.getElementById('unluckyResults'); resultsDiv.style.display = 'block'; resultsDiv.innerHTML = ''; if (scope === 'all') { showLeagueInputForm(resultsDiv); } else if (scope === 'current') { const leagueId = getCurrentLeagueId(); const leagueType = getCurrentLeagueType(); if (!leagueId || !leagueType) { resultsDiv.innerHTML = '

Could not detect league ID or type from URL.

'; showBackButton(resultsDiv); return; } showSidPrompt(resultsDiv, false); } else if (scope === 'specific') { showSidPrompt(resultsDiv, true); } } function initializeScript() { createModal(); addUnluckyButton(); log("MZ-Unlucky initialized - 私たちは最高の防御を持っています!", "success"); } if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(initializeScript, 500); } else { window.addEventListener('load', () => { setTimeout(initializeScript, 500); }); } })();