mtg-playerinfo 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,662 +1,778 @@
1
- <meta name="player-id" content="m4VSTJShiXR1PCSCWaM9TBY0rcg1"/><!DOCTYPE html><html lang="en" data-bs-theme="dark"><head><meta charset="utf-8"><title>Björn Kimminich - Profile</title><!-- SEO Meta Tags--><meta name="description" content="Björn Kimminich's tournament profile and stats"><meta name="keywords" content="magic, magic the gathering, cedh, edh, competitive, commander, events, tournament, software, pairing software"><meta name="author" content="Zain Nayer"><!-- Viewport--><meta name="viewport" content="width=device-width, initial-scale=1"><!-- PWA meta tags--><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="TopDeck.gg"><!-- Font Awesome - Async load to prevent render blocking--><link rel="preconnect" href="https://kit.fontawesome.com" crossorigin><link rel="preconnect" href="https://ka-f.fontawesome.com" crossorigin><script src="https://kit.fontawesome.com/**********.js" crossorigin="anonymous" async></script><!-- Manifest--><link rel="manifest" href="/manifest.json"><!-- Apple App Store Banner--><meta name="apple-itunes-app" content="app-id=6581480935"><!-- Favicon and Touch Icons--><link rel="apple-touch-icon" sizes="192x192" href="/icons/192x192.png"><link rel="icon" type="image/png" sizes="32x32" href="/topdeck-favicon/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/topdeck-favicon/favicon-16x16.png"><!-- Meta Tags Media--><meta name="theme-color" content="#6ca6ea"><meta name="og:title" content="Björn Kimminich - Profile"><meta name="og:description" content="Björn Kimminich's tournament profile and stats"><meta property="og:image" content="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover"><meta name="twitter:title" content="Björn Kimminich - Profile"><meta property="twitter:image" content="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@TopDeckGG_"><!-- Global site tag (gtag.js) - Google Analytics--><script async="" src="https://www.googletagmanager.com/gtag/js?id=*-**********"></script><script>window.dataLayer = window.dataLayer || [];
2
- function gtag(){dataLayer.push(arguments);}
3
- gtag('js', new Date());
4
- gtag('config', '*-**********');
5
- </script><!-- Scroll Bar--><style>::-webkit-scrollbar {
6
- width: 5px;
7
- height: 5px;
8
- }
9
- ::-webkit-scrollbar-thumb {
10
- border-radius: 3px;
11
- background-color: #6ca6ea !important;
12
- }
13
- ::-webkit-scrollbar-thumb:hover {
14
- background-color: #355072 !important;
15
- }</style><style>.notification-item {
16
- cursor: pointer;
17
- }
18
- </style><!-- Page loading styles--><style>.page-loading {
19
- position: fixed;
20
- top: 0;
21
- right: 0;
22
- bottom: 0;
23
- left: 0;
24
- width: 100%;
25
- height: 100%;
26
- -webkit-transition: all .4s .2s ease-in-out;
27
- transition: all .4s .2s ease-in-out;
28
- background-color: #000;
29
- opacity: 0;
30
- visibility: hidden;
31
- z-index: 9999;
32
- }
33
- .page-loading.active {
34
- opacity: 1;
35
- visibility: visible;
36
- }
37
- .page-loading-inner {
38
- position: absolute;
39
- top: 50%;
40
- left: 0;
41
- width: 100%;
42
- text-align: center;
43
- -webkit-transform: translateY(-50%);
44
- transform: translateY(-50%);
45
- -webkit-transition: opacity .2s ease-in-out;
46
- transition: opacity .2s ease-in-out;
47
- opacity: 0;
48
- }
49
- .page-loading.active > .page-loading-inner {
50
- opacity: 1;
51
- }
52
- .page-loading-inner > span {
53
- display: block;
54
- font-family: 'Inter', sans-serif;
55
- font-size: 1rem;
56
- font-weight: normal;
57
- color: #737491;
58
- }
59
- .page-spinner {
60
- display: inline-block;
61
- width: 2.75rem;
62
- height: 2.75rem;
63
- margin-bottom: .75rem;
64
- vertical-align: text-bottom;
65
- border: .15em solid #6ca6ea;
66
- border-right-color: transparent;
67
- border-radius: 50%;
68
- -webkit-animation: spinner .75s linear infinite;
69
- animation: spinner .75s linear infinite;
70
- }
71
- @-webkit-keyframes spinner {
72
- 100% {
73
- -webkit-transform: rotate(360deg);
74
- transform: rotate(360deg);
75
- }
76
- }
77
- @keyframes spinner {
78
- 100% {
79
- -webkit-transform: rotate(360deg);
80
- transform: rotate(360deg);
81
- }
82
- }
83
- </style><!-- Page loading scripts--><script>(function () {
84
- window.onload = function () {
85
- var preloader = document.querySelector('.page-loading');
86
- preloader.classList.remove('active');
87
- };
88
- })();
89
- </script><!-- Vendor Styles--><link rel="stylesheet" media="screen" href="/vendor/simplebar/dist/simplebar.min.css"/><!-- Custom Styles--><!-- Main Theme Styles + Bootstrap--><link rel="stylesheet" media="screen" href="/css/theme.min.css?v2.2"><link rel="stylesheet" href="/css/etemplate.css?v1.1"><!-- Enhancement layer (loads after theme to override)--><!-- Firebase--><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-app.js" defer data-cfasync="false"></script><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-auth.js" defer data-cfasync="false"></script><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-firestore.js" defer data-cfasync="false"></script><script src="/js/cookie.js" defer data-cfasync="false"></script><!-- Moment - Deferred to prevent blocking--><script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script><!-- Jquery - Conditionally deferred on Event Hub only--><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script><!-- Notyf--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css"><script src="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js" defer></script></head><!-- Body--><body style="background-color:#0a0e17;"><!-- Page loading spinner--><div class="page-loading active"><div class="page-loading-inner"><div class="page-spinner"></div><span>Loading...</span></div></div><div class="offcanvas offcanvas-end" id="notificationOffcanvas" tabindex="-1"><div class="offcanvas-header"><h5 class="offcanvas-title">Notifications</h5><button class="btn-close btn-close-white" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body"></div></div><main class="page-wrapper"><!-- Sign In Modal--><div class="modal fade" id="modal-signin" tabindex="-1"><div class="modal-dialog modal-dialog-centered" role="document"><div class="modal-content border-0"><div class="view show" id="modal-signin-view"><div class="modal-header border-0 bg-primary px-4"><h4 class="modal-title text-light">Sign in</h4><button class="btn-close btn-close-white" type="button" data-bs-dismiss="modal" aria-label="btn-close "></button></div><div class="modal-body px-4 bg-dark"><form class="needs-validation" id="LoginForm" novalidate><div class="mb-3"><div class="input-group"><i class="ai-mail position-absolute top-50 start-0 translate-middle-y ms-3"></i><input class="form-control rounded" id="loginEmail" type="email" placeholder="Email" required></div></div><div class="mb-3"><div class="input-group"><i class="ai-lock position-absolute top-50 start-0 translate-middle-y ms-3"></i><div class="password-toggle w-100"><input class="form-control" id="loginPassword" type="password" placeholder="Password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div></div></div><div class="d-flex justify-content-between align-items-center mb-3 mb-3"><div class="form-check"><input class="form-check-input" type="checkbox" id="keep-signed"><label class="form-check-label fs-sm" for="keep-signed">Keep me signed in</label></div><a class="nav-link-style fs-ms" href="/forgot-password">Forgot password?</a></div><button class="btn btn-primary d-block w-100" id="loginSubmit" type="submit">Sign in</button><p class="fs-sm pt-3 mb-0">Don't have an account? <a href='#' class='fw-medium' data-view='#modal-signup-view'>Sign up</a></p></form></div></div><div class="view" id="modal-signup-view"><div class="modal-header border-0 bg-primary px-4"><h4 class="modal-title text-light">Sign up</h4><button class="btn-close btn-close-white" type="button" data-bs-dismiss="modal" aria-label="btn-close"></button></div><div class="modal-body px-4 bg-dark"><p class="fs-ms text-muted">Registration takes less than a minute!</p><form class="needs-validation" id="RegisterForm" novalidate><div class="mb-3"><input class="form-control" id="regName" type="text" placeholder="Full name" required></div><div class="mb-3"><input class="form-control" id="regEmail" type="email" placeholder="Email" required></div><div class="mb-3 password-toggle"><input class="form-control" id="regPass1" type="password" placeholder="Password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div><div class="mb-3 password-toggle"><input class="form-control" id="regPass2" type="password" placeholder="Confirm password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div><div class="mb-3 form-check"><input class="form-check-input" id="regAgree" type="checkbox" required><label class="form-check-label">I agree to the <a href="/privacy-policy" target="_blank">Privacy Policy </a>and <a href="/terms-and-conditions" target="_blank">Terms and Conditions</a></label></div><div class="mb-3 form-check"><input class="form-check-input" id="regNewsletter" type="checkbox" checked><label class="form-check-label">Subscribe to our <a href="https://newsletter.topdeck.gg/">monthly newsletter</a></label></div><button class="btn btn-primary d-block w-100" id="regSubmit" type="submit">Sign up</button><p class="text-danger fs-sm pt-1 mb-0" id="signup-error" style="display: none"></p><p class="fs-sm pt-3 mb-0">Already have an account? <a href='#' class='fw-medium' data-view='#modal-signin-view'>Sign in</a></p></form></div></div></div></div></div><script src="/js/account/login.js?v1.4"></script><!-- Navbar (Solid background)--><!-- Remove "navbar-sticky" class to make navigation bar scrollable with the page.--><header class="header navbar navbar-expand-lg navbar-light bg-dark navbar-sticky" data-scroll-header data-fixed-element><div class="container px-0 px-xl-3"><button class="navbar-toggler ms-n2 me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#primaryMenu"><span class="navbar-toggler-icon"></span></button><a class="navbar-brand flex-shrink-0 order-lg-1 mx-auto ms-lg-0 pe-lg-2 me-lg-4" href="/"><img class="d-none d-lg-block" src="/img/logo/TopDeckNoBorder.png" alt="TopDeck.gg" width="58"><img class="d-lg-none" src="/img/logo/TopDeckFullWhiteNS.png" alt="TopDeck.gg" width="150"></a><div class="d-flex align-items-center order-lg-3 ms-lg-auto"><a class="nav-link-style text-nowrap" href="#modal-signin" data-bs-toggle="modal" data-view="#modal-signin-view"><i class="ai-user fs-xl me-2 align-middle"></i>Sign in</a><a class="btn btn-primary ms-grid-gutter d-none d-lg-inline-block" href="#modal-signin" data-bs-toggle="modal" data-view="#modal-signup-view">Sign up</a></div><div class="offcanvas offcanvas-collapse order-lg-2" id="primaryMenu"><div class="offcanvas-header navbar-shadow"><h5 class="mt-1 mb-0">Menu</h5><button class="btn-close btn-close-white lead" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body"><!-- Menu--><ul class="navbar-nav"><li class="nav-item dropdown dropdown-mega"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Events</a><div class="dropdown-menu"><div class="dropdown-column mb-0"><h5 class="dropdown-header">Browse Events</h5><a class="dropdown-item" href="/">All Events</a><a class="dropdown-item" href="/magic-the-gathering">Magic: The Gathering</a><a class="dropdown-item" href="/pokemon">Pokémon</a><a class="dropdown-item" href="/yu-gi-oh">Yu-Gi-Oh!</a><a class="dropdown-item" href="/one-piece">One Piece</a><a class="dropdown-item" href="/gundam">Gundam TCG</a><a class="dropdown-item" href="/star-wars-unlimited">Star Wars Unlimited</a><a class="dropdown-item" href="/disney-lorcana">Disney Lorcana</a></div><div class="dropdown-column mb-0"><h5 class="dropdown-header d-none d-lg-block">&nbsp;</h5><a class="dropdown-item" href="/riftbound">Riftbound</a><a class="dropdown-item" href="/marvel-snap">Marvel Snap</a><a class="dropdown-item" href="/naruto">Naruto Mythos TCG</a><a class="dropdown-item" href="/world-of-kylia">Hadrai'da Ascendant</a><a class="dropdown-item" href="/beyblade">Beyblade</a><a class="dropdown-item" href="/cardfight-vanguard">Cardfight Vanguard</a><a class="dropdown-item" href="/digimon">Digimon</a><a class="dropdown-item" href="/flesh-and-blood">Flesh and Blood</a></div><div class="dropdown-column mb-0"><h5 class="dropdown-header d-none d-lg-block">&nbsp;</h5><a class="dropdown-item" href="/shadowverse-evolve">Shadowverse: Evolve</a><a class="dropdown-item" href="/sorcery-contested-realm">Sorcery: Contested Realm</a><a class="dropdown-item" href="/altered">Altered</a><a class="dropdown-item" href="/rush-of-ikorr">Rush of Ikorr</a><a class="dropdown-item" href="/cookierun-braverse">CookieRun: Braverse</a><a class="dropdown-item" href="/lairen-la-historia">Lairen: La Historia</a><a class="dropdown-item" href="/settlers-of-catan">Settlers of Catan</a><a class="dropdown-item" href="/gudnak">Gudnak</a></div></div></li><li class="nav-item"><a class="nav-link" href="/subscribe">Subscribe</a></li><li class="nav-item"><a class="nav-link" href="/hubs">Find Stores</a></li><li class="nav-item dropdown dropdown-mega"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Leaderboards</a><div class="dropdown-menu"><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">Magic: The Gathering</h5><a class="dropdown-item" href="/elo/magic-the-gathering/edh">EDH</a><a class="dropdown-item" href="/elo/magic-the-gathering/pauper-edh">Pauper EDH</a><a class="dropdown-item" href="/elo/magic-the-gathering/standard">Standard</a><a class="dropdown-item" href="/elo/magic-the-gathering/pioneer">Pioneer</a><a class="dropdown-item" href="/elo/magic-the-gathering/modern">Modern</a><a class="dropdown-item" href="/elo/magic-the-gathering/legacy">Legacy</a><a class="dropdown-item" href="/elo/magic-the-gathering/pauper">Pauper</a><a class="dropdown-item" href="/elo/magic-the-gathering/vintage">Vintage</a><a class="dropdown-item" href="/elo/magic-the-gathering/premodern">Premodern</a><a class="dropdown-item" href="/elo/magic-the-gathering/limited">Limited</a><a class="dropdown-item" href="/elo/magic-the-gathering/canadian-highlander">Canadian Highlander</a><a class="dropdown-item" href="/elo/magic-the-gathering/duel-commander">Duel Commander</a><a class="dropdown-item" href="/elo/magic-the-gathering/tiny-leaders">Tiny Leaders</a><a class="dropdown-item" href="/elo/magic-the-gathering/7pt-highlander">7pt Highlander</a><a class="dropdown-item" href="/championship-series-2026/leaderboard">Championship Series 2026</a></div><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">Star Wars Unlimited</h5><a class="dropdown-item" href="/elo/star-wars-unlimited/premier">Premier</a><a class="dropdown-item" href="/elo/star-wars-unlimited/draft">Draft</a><a class="dropdown-item" href="/elo/star-wars-unlimited/twin-suns">Twin Suns</a><div class="dropdown-divider"></div><h5 class="dropdown-header my-2">Yu-Gi-Oh!</h5><a class="dropdown-item" href="/elo/yu-gi-oh/advanced">Advanced</a><a class="dropdown-item" href="/elo/yu-gi-oh/edison">Edison</a><a class="dropdown-item" href="/elo/yu-gi-oh/goat">Goat</a><a class="dropdown-item" href="/elo/yu-gi-oh/domain">Domain</a><div class="dropdown-divider"></div><h5 class="dropdown-header my-2">Pokémon</h5><a class="dropdown-item" href="/elo/pokemon/standard">Standard</a><a class="dropdown-item" href="/elo/pokemon/expanded">Expanded</a><a class="dropdown-item" href="/elo/pokemon/legacy">Legacy</a></div><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">More Games</h5><a class="dropdown-item" href="/elo/marvel-snap">Marvel Snap</a><a class="dropdown-item" href="/elo/lorcana">Disney Lorcana</a><a class="dropdown-item" href="/elo/one-piece">One Piece</a><a class="dropdown-item" href="/elo/digimon">Digimon</a><a class="dropdown-item" href="/elo/shadowverse-evolve">Shadowverse Evolve</a><a class="dropdown-item" href="/elo/flesh-and-blood">Flesh and Blood</a><a class="dropdown-item" href="/elo/sorcery-contested-realm">Sorcery: Contested Realm</a><a class="dropdown-item" href="/elo/altered/standard">Altered: Standard</a><a class="dropdown-item" href="/elo/altered/draft">Altered: Draft</a><a class="dropdown-item" href="/elo/beyblade/x">Beyblade X</a></div></div></li><li class="nav-item"><a class="nav-link" href="/championship-series-2026">Championship Series</a></li><li class="nav-item"><a class="nav-link" href="https://shop.topdeck.gg/" target="_blank">Merch</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">More</a><ul class="dropdown-menu"><li><h6 class="dropdown-header">Community</h6></li><li><a class="dropdown-item" href="https://newsletter.topdeck.gg/" target="_blank"><i class="fa-solid fa-envelope opacity-60 me-2"></i>Newsletter</a></li><li><a class="dropdown-item" href="/blog"><i class="fa-solid fa-newspaper opacity-60 me-2"></i>What's New</a></li><li><a class="dropdown-item" href="https://discord.topdeck.gg/" target="_blank"><i class="fa-brands fa-discord opacity-60 me-2"></i>Discord Bot</a></li><div class="dropdown-divider"></div><li><h6 class="dropdown-header">Resources</h6></li><li><a class="dropdown-item" href="/docs/tournaments-v2"><i class="fa-solid fa-code opacity-60 me-2"></i>API Documentation</a></li><li><a class="dropdown-item" href="/help"><i class="fa-solid fa-circle-question opacity-60 me-2"></i>Help Center</a></li><div class="dropdown-divider"></div><li><h6 class="dropdown-header">Mobile App</h6></li><li><a class="dropdown-item" href="https://apple.co/3A4Z0FK" target="_blank"><i class="fa-brands fa-apple opacity-60 me-2"></i>Download on iOS</a></li><li><a class="dropdown-item" href="https://play.google.com/store/apps/details?id=com.topdeck.app" target="_blank"><i class="fa-brands fa-google-play opacity-60 me-2"></i>Download on Android</a></li></ul></li></ul></div></div></div></header><script>function EndSession(){
90
- fetch("/endSession", {
91
- method: "POST",
92
- headers: {
93
- "Accept": "application/json",
94
- "Content-Type": "application/json",
95
- },
96
- }).then((ret) => {
97
- location.reload();
98
- });
99
- }</script><!-- Page content--><div class="offcanvas offcanvas-end w-100" id="viewDecklistOffcanvas" tabindex="-1" aria-labelledby="viewDecklistOffcanvasLabel" aria-hidden="true"><div class="offcanvas-header bg-primary"><h5 class="offcanvas-title" id="viewDecklistOffcanvasLabel">Decklist</h5><button class="btn-close btn-close-white" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body bg-dark"><pre class="mb-3"><code class="language-json" id="decklistContent"></code></pre></div></div><script src="/js/supportedGames?t=1770337214287"></script><script>function viewTextDecklist(decklist, title, tournamentId = null, playerId = null, game = null) {
100
- // Use dynamically served games list
101
- const supportedGames = window.SUPPORTED_GAMES;
102
-
103
- // Check if we should use the new deck page
104
- if (game && supportedGames.has(game) && tournamentId && playerId) {
105
- // Open the new deck page
106
- const deckUrl = `/deck/${tournamentId}/${playerId}`;
107
- window.open(deckUrl, '_blank');
108
- return;
109
- }
110
-
111
- // Handle different decklist formats for offcanvas fallback
112
- let formattedDecklist;
113
- if (typeof decklist === 'string') {
114
- // Try the more comprehensive unescaping first if available
115
- if (typeof unescapeDecklistFromStorage === 'function') {
116
- formattedDecklist = unescapeDecklistFromStorage(decklist);
117
- } else {
118
- // Fallback to simple newline replacement
119
- formattedDecklist = decklist.replace(/\\n/g, '\n');
120
- }
121
- } else {
122
- formattedDecklist = String(decklist);
123
- }
124
-
125
- // Set content and title
126
- document.getElementById('decklistContent').textContent = formattedDecklist;
127
- document.getElementById('viewDecklistOffcanvasLabel').textContent = title + ' - Decklist';
128
-
129
- // Show the offcanvas
130
- new bootstrap.Offcanvas(document.getElementById('viewDecklistOffcanvas')).show();
131
- }
132
- </script><style>@media (min-width: 768px) {
133
- #viewDecklistOffcanvas {
134
- width: 580px !important;
135
- }
136
- }
137
-
138
- @media (min-width: 1200px) {
139
- #viewDecklistOffcanvas {
140
- width: 660px !important;
141
- }
142
- }</style><!-- Hero Section with Cover Photo--><div class="position-relative overflow-hidden"><div class="position-relative" style="height: 280px; background-image: url(https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover); background-size: cover; background-position: center;"><div class="position-absolute top-0 start-0 w-100 h-100 bg-dark" style="opacity: 0.2"></div></div><!-- Decorative wave divider--><div class="position-absolute bottom-0 w-100"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 100" preserveAspectRatio="none" style="height: 50px; width: 100%;"><path fill="#0a0e17" d="M0,32L60,42.7C120,53,240,75,360,74.7C480,75,600,53,720,42.7C840,32,960,32,1080,37.3C1200,43,1320,53,1380,58.7L1440,64L1440,100L1380,100C1320,100,1200,100,1080,100C960,100,840,100,720,100C600,100,480,100,360,100C240,100,120,100,60,100L0,100Z"></path></svg></div></div><!-- Profile Card--><div class="container position-relative"><div class="card bg-dark text-white shadow-lg border-0 rounded-4 mt-n5 position-relative" style="z-index: 2;"><div class="card-body py-4 px-md-4"><div class="row align-items-start"><!-- Avatar Column--><div class="col-lg-2 col-md-3 text-center text-md-start mb-4 mb-md-0"><div class="position-relative mx-auto" style="width: fit-content;"><img class="rounded-circle shadow-lg" src="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/2a7b8d12-5924-4a58-5f9c-c0bf55766800/square" alt="Profile Picture" style="width: 140px; height: 140px; object-fit: cover; margin-top: -70px; border: 4px solid #0a0e17; background-color: #0a0e17;"></div></div><!-- Player Info & About--><div class="col-lg-5 col-md-9 my-sm-auto mb-4"><h2 class="text-white fw-bold mb-1">Björn Kimminich</h2><div class="d-flex flex-wrap mt-2 gap-2"><a class="btn btn-social bs-outline bs-twitter bs-dark me-2" href="https://twitter.com/bkimminich" target="_blank"><i class="fa-brands fa-twitter"></i></a></div><div class="d-flex flex-wrap gap-2 mt-3" id="badgeContainer"></div></div><!-- Stats Card--><div class="col-lg-5 mx-auto"><div class="card bg-dark border-0 shadow-lg rounded-4 position-relative overflow-hidden"><!-- Decorative gradient background--><div class="position-absolute top-0 start-0 w-100 h-100" style="background: linear-gradient(45deg, rgba(90, 79, 158, 0.2), rgba(0, 0, 0, 0)); opacity: 0.7; z-index: 0;"></div><div class="card-body position-relative p-4" style="z-index: 1;"><h4 class="text-white fw-bold mb-4" id="statsTitle">Player Stats</h4><div class="row g-3"><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-trophy text-warning fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="totalTournaments">0</h5><p class="text-light small mb-0">Tournaments</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-chart-line text-success fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="overallRecord">0-0-0</h5><p class="text-light small mb-0">Record</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-percentage text-info fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="overallWinRate">0%</h5><p class="text-light small mb-0">Win Rate</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-bolt text-danger fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="conversionRate">0%</h5><p class="text-light small mb-0">Conversion</p></div></div></div></div></div></div></div></div></div></div><!-- Modern Pill Tabs--><div class="card bg-dark border-0 shadow-sm rounded-4 mt-4 p-3"><div class="card-body p-0"><ul class="nav nav-pills gap-2 mb-0" role="tablist"><li class="nav-item"><a class="nav-link active py-3 rounded-3" href="#history" data-bs-toggle="tab"><i class="fa-solid fa-trophy me-2"></i>Tournaments</a></li></ul></div></div><!-- Content tabs--><div class="tab-content mt-4 mb-5"><!-- Tournament History Tab--><div class="tab-pane fade show active" id="history"><div class="card bg-dark border-0 rounded-4 shadow-lg py-4 px-3 px-sm-4"><!-- Filters Section--><div class="d-flex flex-wrap gap-3 mb-4 align-items-center"><!-- Format Dropdown - Modern style--><div class="dropdown"><button class="btn btn-outline-secondary dropdown-toggle" id="formatDropdown" type="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-layer-group me-2"></i><span id="formatDropdownLabel">All Formats</span></button><ul class="dropdown-menu border-0 shadow"><li><a class="dropdown-item" href="#" data-value="all">All Formats</a></li></ul></div><!-- Year Dropdown - Modern style--><div class="dropdown"><button class="btn btn-outline-secondary dropdown-toggle" id="yearDropdown" type="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-calendar-alt me-2"></i><span id="yearDropdownLabel">All Years</span></button><ul class="dropdown-menu border-0 shadow"><li><a class="dropdown-item" href="#" data-value="all">All Years</a></li></ul></div></div><h3 class="card-title text-white fw-bold"><i class="fa-solid fa-history me-2 text-info"></i>Tournament History</h3><div class="tournament-list" id="tournamentList"></div></div></div></div></div></main><footer class="footer bg-black py-5"><div class="container pt-md-2 pt-lg-3"><div class="row pt-sm-2"><!-- Logo, description, and social column--><div class="col-md-4 col-lg-3 pb-2 pb-md-0 mb-4 mb-md-0"><a class="d-block mb-3 mb-md-4" href="/"><img class="img-fluid" src="/img/logo/TopDeckFullWhiteNS.png" alt="TopDeck.gg" style="max-width: 200px;"></a><p class="text-muted pb-2 pb-md-3 mb-3 fs-sm">Discover, organize, and compete in trading card game events. Connect with players worldwide and elevate your game.</p><div class="d-flex gap-1"><a class="btn-social bs-outline bs-light bs-facebook bs-lg me-2" href="https://discord.gg/RjS3mDf3wt" target="_blank"><i class="fa-brands fa-discord"></i></a><a class="btn-social bs-outline bs-light bs-youtube bs-lg me-2" href="https://www.youtube.com/@TopDeckGG_" target="_blank"><i class="fa-brands fa-youtube"></i></a></div></div><!-- Nested columns for links--><div class="col-md-8 col-lg-7 offset-lg-2"><div class="row row-cols-1 row-cols-sm-3"><!-- Company column--><div class="col mb-4 mb-md-0"><div class="widget widget-light"><h4 class="widget-title mb-3">Company</h4><ul><li><a class="widget-link" href="/subscribe">Run a Tournament</a></li><li><a class="widget-link" href="https://newsletter.topdeck.gg/">Newsletter</a></li><li><a class="widget-link" href="/docs/tournaments-v2">API Documentation</a></li><li><a class="widget-link" href="/championship-series-2026">Championship Series</a></li></ul></div></div><!-- Support column--><div class="col mb-4 mb-md-0"><div class="widget widget-light"><h4 class="widget-title mb-3">Support</h4><ul><li><a class="widget-link" href="/cdn-cgi/l/email-protection#89eae6e7fde8eafdc9fde6f9edeceae2a7eeee">Contact Us</a></li><li><a class="widget-link" href="/cdn-cgi/l/email-protection#88e5edece1e9c8fce7f8ecedebe3a6efef">Media Inquiries</a></li><li><a class="widget-link" href="/privacy-policy" target="_blank">Privacy Policy</a></li><li><a class="widget-link" href="/terms-and-conditions" target="_blank">Terms & Conditions</a></li></ul></div></div><!-- Install App column--><div class="col"><h4 class="h6 fw-bold pb-2 mb-0 mb-lg-1">Install App</h4><a class="btn btn-outline-secondary d-inline-flex align-items-center px-3 py-2 mt-3 me-3 me-md-0" href="https://apple.co/3A4Z0FK" role="button" style="min-width: 160px;"><i class="fa-brands fa-apple fs-xl me-2"></i><span class="d-flex flex-column align-items-start"><small class="fs-xs text-white-50">Download on the</small><span class="fs-sm">App Store</span></span></a><a class="btn btn-outline-secondary d-inline-flex align-items-center px-3 py-2 mt-3 me-3 me-md-0" href="https://play.google.com/store/apps/details?id=com.topdeck.app" role="button" style="min-width: 160px;"><i class="fa-brands fa-google-play fs-xl me-2"></i><span class="d-flex flex-column align-items-start"><small class="fs-xs text-white-50">Get it on</small><span class="fs-sm">Google Play</span></span></a></div></div></div></div></div></footer><script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>document.addEventListener('DOMContentLoaded', function () {
143
- // Template variables for decklist viewing
144
- const playerUsername = "@k0shiii";
145
- const playerId = "m4VSTJShiXR1PCSCWaM9TBY0rcg1";
146
-
147
- // Initialize UI with loading states
148
- let statsTitle = document.getElementById('statsTitle');
149
- const badgeContainer = document.getElementById('badgeContainer');
150
- const tournamentList = document.getElementById('tournamentList');
151
-
152
- // Add loading indicators using Bootstrap 5
153
- document.getElementById('statsTitle').parentElement.classList.add('position-relative');
154
- document.getElementById('statsTitle').parentElement.innerHTML += `
155
- <div class="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75" style="z-index: 10; border-radius: 0.5rem;">
156
- <div class="spinner-border text-primary" role="status">
157
- <span class="visually-hidden">Loading...</span>
158
- </div>
159
- </div>
160
- `;
161
-
162
- tournamentList.innerHTML = `
163
- <div class="text-center py-5">
164
- <div class="spinner-border text-primary mb-3" role="status">
165
- <span class="visually-hidden">Loading...</span>
166
- </div>
167
- <p class="text-muted mb-0">Loading tournament data...</p>
168
- </div>
169
- `;
170
-
171
- // Store these as global variables to be used after data load
172
- let gameFormats = {};
173
- let topFinishes = {};
174
- let yearlyStats = {};
175
- let tdcsData = null;
176
-
177
- let selectedFormat = 'all';
178
- let selectedYear = 'all';
179
-
180
- // Fetch player data from API
181
- fetchPlayerData(playerId);
182
-
183
- // API function to get player data
184
- async function fetchPlayerData(playerId) {
185
- try {
186
- const response = await fetch(`/profile/${playerId}/stats`);
187
- if (!response.ok) {
188
- throw new Error('Failed to fetch player data');
189
- }
190
-
191
- const data = await response.json();
192
-
193
- // Store the data globally
194
- gameFormats = data.gameFormats || {};
195
- topFinishes = data.topFinishes || {};
196
- yearlyStats = data.yearlyStats || {};
197
- tdcsData = data.tdcsData || null;
198
-
199
- // Remove loading indicators
200
- const loadingOverlay = document.getElementById('statsTitle').parentElement.querySelector('.position-absolute');
201
- if (loadingOverlay) {
202
- loadingOverlay.remove();
203
- }
204
-
205
- statsTitle = document.getElementById('statsTitle');
206
-
207
- // Initialize the UI with the data
208
- initializeUI();
209
-
210
- // Populate the UI with data
211
- updateStatistics();
212
-
213
- } catch (error) {
214
- console.error('Error fetching player data:', error);
215
- tournamentList.innerHTML = `
216
- <div class="alert alert-danger">
217
- <i class="fa-solid fa-exclamation-circle me-2"></i>
218
- Failed to load tournament data. Please try refreshing the page.
219
- </div>
220
- `;
221
- }
222
- }
223
-
224
- function initializeUI() {
225
- // Initialize dropdowns with available formats and years
226
- populateDropdowns();
227
-
228
- // Set up event handlers
229
- setupEventHandlers();
230
- }
231
-
232
- function populateDropdowns() {
233
- // Format dropdown
234
- const formatDropdown = document.querySelector('#formatDropdown + .dropdown-menu');
235
- formatDropdown.innerHTML = '<li><a class="dropdown-item" href="#" data-value="all">All Formats</a></li>';
236
-
237
- Object.keys(gameFormats).forEach(format => {
238
- const li = document.createElement('li');
239
- li.innerHTML = `<a class="dropdown-item" href="#" data-value="${format}">${format}</a>`;
240
- formatDropdown.appendChild(li);
241
- });
242
-
243
- // Year dropdown
244
- const yearDropdown = document.querySelector('#yearDropdown + .dropdown-menu');
245
- yearDropdown.innerHTML = '<li><a class="dropdown-item" href="#" data-value="all">All Years</a></li>';
246
-
247
- if (yearlyStats) {
248
- Object.keys(yearlyStats).sort((a, b) => b - a).forEach(year => {
249
- const li = document.createElement('li');
250
- li.innerHTML = `<a class="dropdown-item" href="#" data-value="${year}">${year}</a>`;
251
- yearDropdown.appendChild(li);
252
- });
253
- }
254
- }
255
-
256
- function setupEventHandlers() {
257
- // Dropdown click handlers
258
- document.querySelectorAll('#formatDropdown + .dropdown-menu a').forEach(item => {
259
- item.addEventListener('click', e => {
260
- e.preventDefault();
261
- selectedFormat = item.getAttribute('data-value');
262
- document.getElementById('formatDropdownLabel').textContent = item.textContent;
263
- updateStatistics();
264
- });
265
- });
266
-
267
- document.querySelectorAll('#yearDropdown + .dropdown-menu a').forEach(item => {
268
- item.addEventListener('click', e => {
269
- e.preventDefault();
270
- selectedYear = item.getAttribute('data-value');
271
- document.getElementById('yearDropdownLabel').textContent = item.textContent;
272
- updateStatistics();
273
- });
274
- });
275
- }
276
-
277
- function updateStatistics() {
278
- updateTitles(selectedFormat, selectedYear);
279
- updateFormatStats(selectedFormat, selectedYear);
280
- updateTournamentList(selectedFormat, selectedYear);
281
- updateTdcsTab();
282
- }
283
-
284
- function updateTitles(format, year) {
285
- const formatText = format === 'all' ? 'Player' : format;
286
- const yearText = year === 'all' ? '' : ` ${year}`;
287
- statsTitle.textContent = `${formatText} Stats${yearText}`;
288
- }
289
-
290
- function updateFormatStats(format, year) {
291
- // Clear badges first - this ensures badges are completely reset when changing formats/years
292
- badgeContainer.innerHTML = '';
293
-
294
- let stats;
295
- if (year === 'all') {
296
- stats = format === 'all' ? calculateOverallStats() : calculateFormatStats(format);
297
- } else {
298
- // When a specific year is selected
299
- if (format === 'all') {
300
- // All formats for a specific year
301
- stats = yearlyStats[year]?.overall;
302
- } else {
303
- // Specific format for a specific year
304
- stats = yearlyStats[year]?.[format];
305
- }
306
- }
307
-
308
- if (!stats) {
309
- // If no stats for the selected format/year combination, show zeros
310
- document.getElementById('totalTournaments').textContent = '0';
311
- document.getElementById('overallRecord').textContent = '0-0-0';
312
- document.getElementById('overallWinRate').textContent = '0.00%';
313
- document.getElementById('conversionRate').textContent = '0.00%';
314
- return;
315
- }
316
-
317
- document.getElementById('totalTournaments').textContent = stats.totalTournaments;
318
- document.getElementById('overallRecord').textContent = `${stats.wins}-${stats.losses}-${stats.draws}`;
319
- const totalGames = stats.wins + stats.losses + stats.draws;
320
- const winRate = totalGames > 0 ? ((stats.wins / totalGames) * 100).toFixed(2) + '%' : '0.00%';
321
- document.getElementById('overallWinRate').textContent = winRate;
322
-
323
- const topFinishesCount = stats.firstPlaceFinishes + stats.top2 + stats.top4 + stats.top8 + stats.top10 + stats.top16;
324
- const conversionRate = stats.topCutEligible > 0 ? ((topFinishesCount / stats.topCutEligible) * 100).toFixed(2) + '%' : '0.00%';
325
- document.getElementById('conversionRate').textContent = conversionRate;
326
-
327
- const createBadge = (label, value, color) => {
328
- if (value > 0) {
329
- const badge = document.createElement('span');
330
- badge.className = 'badge me-1 mb-2';
331
- badge.style.cssText = `background-color: ${color}; color: #1e1e1e !important;`;
332
- badge.innerHTML = `<i class="fa-solid fa-medal me-1"></i>${label}: ${value}`;
333
- badgeContainer.appendChild(badge);
334
- }
335
- };
336
-
337
- // Only create badges if there are stats
338
- createBadge('Wins', stats.firstPlaceFinishes, '#ffc321');
339
- createBadge('Finals', stats.top2 + stats.top4, '#C0C0C0');
340
- createBadge('Top 8', stats.top8, '#f08035');
341
- createBadge('Top 10', stats.top10, '#3d85c6');
342
- createBadge('Top 16', stats.top16, '#f08035');
343
- }
344
-
345
- function updateTournamentList(format, year) {
346
- tournamentList.innerHTML = '';
347
-
348
- // If no gameFormats, show empty state
349
- if (Object.keys(gameFormats).length === 0) {
350
- tournamentList.innerHTML = `
351
- <div class="text-center py-5">
352
- <p class="text-muted">No tournament data available</p>
353
- </div>
354
- `;
355
- return;
356
- }
357
-
358
- // Filter formats based on selection
359
- const formatsToShow = format === 'all'
360
- ? Object.entries(gameFormats)
361
- : [[format, gameFormats[format]]].filter(([_, tournaments]) => tournaments);
362
-
363
- if (formatsToShow.length === 0) {
364
- tournamentList.innerHTML = `
365
- <div class="text-center py-5">
366
- <p class="text-muted">No tournaments found for the selected criteria</p>
367
- </div>
368
- `;
369
- return;
370
- }
371
-
372
- formatsToShow.forEach(([formatName, tournaments]) => {
373
- // Filter tournaments by year if a specific year is selected
374
- const filteredTournaments = year === 'all'
375
- ? tournaments
376
- : tournaments.filter(t => new Date(t.date).getFullYear().toString() === year);
377
-
378
- if (filteredTournaments.length > 0) {
379
- // Format header with a more modern design
380
- const formatHeader = document.createElement('div');
381
- formatHeader.className = 'd-flex align-items-center mt-4 mb-3';
382
- formatHeader.innerHTML = `
383
- <div class="p-2 rounded-3 me-2" style="background-color: rgba(90, 79, 158, 0.3);">
384
- <i class="fa-solid fa-dice-d20 text-light"></i>
385
- </div>
386
- <h5 class="mb-0 text-white">${formatName}</h5>
387
- `;
388
- tournamentList.appendChild(formatHeader);
389
-
390
- // Create a container for the tournament cards in this format
391
- const cardContainer = document.createElement('div');
392
- cardContainer.className = 'row g-3';
393
- tournamentList.appendChild(cardContainer);
394
-
395
- filteredTournaments.forEach(tournament => {
396
- // Get placement for styling
397
- const placement = parseInt(tournament.placement);
398
- let placementClass = '';
399
- let placementIcon = '';
400
-
401
- if (placement === 1) {
402
- placementClass = 'border-warning';
403
- placementIcon = '<i class="fa-solid fa-trophy text-warning me-1"></i>';
404
- } else if (placement <= 4) {
405
- placementClass = 'border-info';
406
- placementIcon = '<i class="fa-solid fa-medal text-info me-1"></i>';
407
- } else if (placement <= 8) {
408
- placementClass = 'border-primary';
409
- placementIcon = '<i class="fa-solid fa-award text-primary me-1"></i>';
410
- }
411
-
412
- // Tournament card with enhanced styling
413
- const tournamentCol = document.createElement('div');
414
- tournamentCol.className = 'col-12 col-md-6 col-lg-4';
415
-
416
- const tournamentCard = document.createElement('div');
417
- tournamentCard.className = `card h-100 bg-secondary text-white shadow-sm ${placementClass ? 'border-start' : ''} ${placementClass}`;
418
-
419
- // Calculate match percentages for the progress bar
420
- const [wins, losses, draws] = tournament.record.split('-').map(Number);
421
- const matches = wins + losses + draws;
422
- const winPercentage = matches > 0 ? Math.round((wins / matches) * 100) : 0;
423
- const lossPercentage = matches > 0 ? Math.round((losses / matches) * 100) : 0;
424
- const drawPercentage = matches > 0 ? Math.round((draws / matches) * 100) : 0;
425
-
426
- // Format date nicely
427
- const tournamentDate = new Date(tournament.date);
428
- const dateOptions = { year: 'numeric', month: 'short', day: 'numeric' };
429
- const formattedDate = tournamentDate.toLocaleDateString(undefined, dateOptions);
430
-
431
- tournamentCard.innerHTML = `
432
- <div class="card-body d-flex flex-column">
433
- <h6 class="card-title mb-1 text-truncate">${tournament.name}</h6>
434
- <div class="mb-2">
435
- <span class="badge bg-dark text-light border border-light">${formattedDate}</span>
436
- </div>
437
-
438
- <div class="mt-2 mb-3">
439
- <div class="d-flex justify-content-between mb-2">
440
- <div>
441
- ${placementIcon}
442
- <span class="text-light">${tournament.placement} of ${tournament.size}</span>
443
- </div>
444
- <div class="text-light">
445
- ${tournament.record}
446
- </div>
447
- </div>
448
-
449
- <div class="progress rounded-pill" style="height: 8px;" title="${wins} wins, ${losses} losses, ${draws} draws">
450
- ${wins > 0 ? `<div class="progress-bar bg-success" role="progressbar" style="width: ${winPercentage}%" aria-valuenow="${winPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
451
- ${losses > 0 ? `<div class="progress-bar bg-danger" role="progressbar" style="width: ${lossPercentage}%" aria-valuenow="${lossPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
452
- ${draws > 0 ? `<div class="progress-bar bg-warning" role="progressbar" style="width: ${drawPercentage}%" aria-valuenow="${drawPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
453
- </div>
454
- </div>
455
-
456
- <div class="d-flex flex-wrap gap-2 mt-auto">
457
- <a href="${tournament.bracketLink}" target="_blank" class="btn btn-sm btn-outline-secondary">
458
- <i class="fa-solid fa-link me-1"></i>Bracket
459
- </a>
460
- ${tournament.decklist ? `
461
- <button type="button" class="btn btn-sm btn-outline-secondary decklist-btn"
462
- data-decklist="${tournament.decklist}"
463
- data-tournament-name="${tournament.name}"
464
- data-tournament-id="${tournament.id}"
465
- data-format="${formatName}">
466
- <i class="fa-solid fa-cards-blank me-1"></i>Decklist
467
- </button>
468
- ` : ''}
469
- </div>
470
- </div>
471
- `;
472
-
473
- tournamentCol.appendChild(tournamentCard);
474
- cardContainer.appendChild(tournamentCol);
475
- });
476
- }
477
- });
478
-
479
- // If no tournaments were found after filtering
480
- if (tournamentList.innerHTML === '') {
481
- tournamentList.innerHTML = `
482
- <div class="text-center py-5">
483
- <p class="text-muted">No tournaments found for the selected criteria</p>
484
- </div>
485
- `;
486
- }
487
-
488
- // Add event listeners for decklist buttons
489
- document.querySelectorAll('.decklist-btn').forEach(button => {
490
- button.addEventListener('click', function() {
491
- const decklist = this.dataset.decklist;
492
- const tournamentName = this.dataset.tournamentName;
493
- const tournamentId = this.dataset.tournamentId;
494
- const playerIdentifier = playerUsername || playerId;
495
-
496
- const format = this.dataset.format;
497
- const baseGame = format.includes(':') ? format.split(':').slice(0, 2).join(':').trim() : format;
498
-
499
- viewTextDecklist(decklist, tournamentName, tournamentId, playerIdentifier, baseGame);
500
- });
501
- });
502
- }
503
-
504
- function updateTdcsTab() {
505
- // First, update the tab visibility
506
- const tabList = document.querySelector('.nav-pills');
507
- const existingTab = document.querySelector('a[href="#championship"]');
508
-
509
- if (tdcsData) {
510
- // Show the tab if TDCS data exists
511
- if (!existingTab) {
512
- const tabItem = document.createElement('li');
513
- tabItem.className = 'nav-item';
514
- tabItem.innerHTML = `
515
- <a class="nav-link py-3 rounded-3" href="#championship" data-bs-toggle="tab">
516
- <i class="fa-solid fa-award me-2"></i>
517
- Series
518
- </a>
519
- `;
520
- tabList.appendChild(tabItem);
521
- }
522
-
523
- // Create or update the tab content
524
- const tabContent = document.querySelector('.tab-content');
525
- let championshipPane = document.getElementById('championship');
526
-
527
- if (!championshipPane) {
528
- championshipPane = document.createElement('div');
529
- championshipPane.id = 'championship';
530
- championshipPane.className = 'tab-pane fade';
531
- tabContent.appendChild(championshipPane);
532
- }
533
-
534
- championshipPane.innerHTML = `
535
- <div class="card bg-secondary text-white shadow-sm">
536
- <div class="card-body">
537
- <h4 class="card-title text-center mb-4">TopDeck Championship Series 2026</h4>
538
-
539
- <div class="text-center mb-5">
540
- <p class="fs-6 mb-1">Total Points</p>
541
- <h2 class="fw-bold text-highlight">${tdcsData.points}</h2>
542
- </div>
543
-
544
- ${generateTierContent()}
545
- </div>
546
- </div>
547
- `;
548
- } else {
549
- // Remove the tab if no TDCS data
550
- if (existingTab) {
551
- existingTab.parentElement.remove();
552
- }
553
-
554
- const championshipPane = document.getElementById('championship');
555
- if (championshipPane) {
556
- championshipPane.remove();
557
- }
558
- }
559
- }
560
-
561
- function generateTierContent() {
562
- const tiers = ['Diamond', 'Platinum', 'Gold', 'Silver', 'Bronze'];
563
- let content = '';
564
-
565
- tiers.forEach(tier => {
566
- if (tdcsData.tierBreakdown && tdcsData.tierBreakdown[tier] && tdcsData.tierBreakdown[tier].length > 0) {
567
- content += `
568
- <div class="d-flex align-items-center mb-3 mt-5">
569
- <img src="/img/tdcs/new/TD${tier}.png" alt="${tier} Tier" style="height: 40px; width: auto; margin-right: 12px;">
570
- <h5 class="mb-0">${tier} Tier</h5>
571
- </div>
572
-
573
- <div class="row g-3">
574
- `;
575
-
576
- tdcsData.tierBreakdown[tier].forEach(item => {
577
- if (item.tournamentName && item.points > 0) {
578
- content += `
579
- <div class="col-12 col-md-6 col-lg-4">
580
- <div class="card bg-dark text-white h-100 shadow-sm">
581
- <div class="card-body d-flex flex-column justify-content-between">
582
- <div class="mb-3">
583
- <h6 class="card-title mb-1">${item.tournamentName}</h6>
584
- <p class="text-muted mb-0 small">(${item.standing} of ${item.totalPlayers})</p>
585
- </div>
586
- <div class="d-flex justify-content-between align-items-end mt-auto">
587
- <span class="fw-bold">${item.points} pts</span>
588
- </div>
589
- </div>
590
- </div>
591
- </div>
592
- `;
593
- }
594
- });
595
-
596
- content += '</div>';
597
- }
598
- });
599
-
600
- return content;
601
- }
602
-
603
- function calculateOverallStats() {
604
- let overallStats = {
605
- totalTournaments: 0, topCutEligible: 0, wins: 0, losses: 0, draws: 0,
606
- firstPlaceFinishes: 0, top2: 0, top4: 0, top8: 0, top10: 0, top16: 0
607
- };
608
-
609
- Object.values(topFinishes).forEach(formatFinishes => {
610
- Object.keys(overallStats).forEach(key => {
611
- if (key in formatFinishes) {
612
- overallStats[key] += formatFinishes[key];
613
- }
614
- });
615
- });
616
-
617
- Object.values(gameFormats).forEach(tournaments => {
618
- overallStats.totalTournaments += tournaments.length;
619
- tournaments.forEach(t => {
620
- const [wins, losses, draws] = t.record.split('-').map(Number);
621
- overallStats.wins += wins;
622
- overallStats.losses += losses;
623
- overallStats.draws += draws;
624
- if (t.topCut > 0) {
625
- overallStats.topCutEligible++;
626
- }
627
- });
628
- });
629
-
630
- return overallStats;
631
- }
632
-
633
- function calculateFormatStats(format) {
634
- // If the format doesn't exist, return empty stats
635
- if (!gameFormats[format] || !topFinishes[format]) {
636
- return {
637
- totalTournaments: 0, topCutEligible: 0, wins: 0, losses: 0, draws: 0,
638
- firstPlaceFinishes: 0, top2: 0, top4: 0, top8: 0, top10: 0, top16: 0
639
- };
640
- }
641
-
642
- const formatStats = {
643
- ...topFinishes[format],
644
- totalTournaments: gameFormats[format].length,
645
- topCutEligible: 0,
646
- wins: 0, losses: 0, draws: 0
647
- };
648
-
649
- gameFormats[format].forEach(t => {
650
- const [wins, losses, draws] = t.record.split('-').map(Number);
651
- formatStats.wins += wins;
652
- formatStats.losses += losses;
653
- formatStats.draws += draws;
654
- if (t.topCut > 0) {
655
- formatStats.topCutEligible++;
656
- }
657
- });
658
-
659
- return formatStats;
660
- }
661
- });</script><!-- Back to top button--><a class="btn-scroll-top" href="#top" data-scroll data-fixed-element><span class="btn-scroll-top-tooltip text-muted fs-sm me-2">Top</span><i class="btn-scroll-top-icon ai-arrow-up"> </i></a><!-- Vendor scrits: js libraries and plugins--><script src="/vendor/bootstrap/dist/js/bootstrap.bundle.min.js"></script><script src="/vendor/simplebar/dist/simplebar.min.js"></script><script src="/vendor/smooth-scroll/dist/smooth-scroll.polyfills.min.js"></script><!-- Main theme script--><script src="/js/theme.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"version":"2024.11.0","token":"********************************","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
662
- </body></html>
1
+ <meta name="player-id" content="m4VSTJShiXR1PCSCWaM9TBY0rcg1"/><!DOCTYPE html><html lang="en" data-bs-theme="dark"><head><meta charset="utf-8"><title>Björn Kimminich - Profile</title><!-- SEO Meta Tags--><meta name="description" content="Björn Kimminich's tournament profile and stats"><meta name="keywords" content="magic, magic the gathering, cedh, edh, competitive, commander, events, tournament, software, pairing software"><meta name="author" content="Zain Nayer"><!-- Viewport--><meta name="viewport" content="width=device-width, initial-scale=1"><!-- PWA meta tags--><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black"><meta name="apple-mobile-web-app-title" content="TopDeck.gg"><!-- Font Awesome - Async load to prevent render blocking--><link rel="preconnect" href="https://kit.fontawesome.com" crossorigin><link rel="preconnect" href="https://ka-f.fontawesome.com" crossorigin><script src="https://kit.fontawesome.com/3df2a3da01.js" crossorigin="anonymous" async></script><!-- Manifest--><link rel="manifest" href="/manifest.json"><!-- Apple App Store Banner--><meta name="apple-itunes-app" content="app-id=6581480935"><!-- Favicon and Touch Icons--><link rel="apple-touch-icon" sizes="192x192" href="/icons/192x192.png"><link rel="icon" type="image/png" sizes="32x32" href="/topdeck-favicon/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/topdeck-favicon/favicon-16x16.png"><!-- Meta Tags Media--><meta name="theme-color" content="#6ca6ea"><meta name="og:title" content="Björn Kimminich - Profile"><meta name="og:description" content="Björn Kimminich's tournament profile and stats"><meta property="og:image" content="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover"><meta name="twitter:title" content="Björn Kimminich - Profile"><meta property="twitter:image" content="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@TopDeckGG_"><!-- Global site tag (gtag.js) - Google Analytics--><script async="" src="https://www.googletagmanager.com/gtag/js?id=G-56527VG23P"></script><script>window.dataLayer = window.dataLayer || [];
2
+ function gtag(){dataLayer.push(arguments);}
3
+ gtag('js', new Date());
4
+ gtag('config', 'G-56527VG23P');
5
+ </script><!-- Scroll Bar--><style>::-webkit-scrollbar {
6
+ width: 5px;
7
+ height: 5px;
8
+ }
9
+ ::-webkit-scrollbar-thumb {
10
+ border-radius: 3px;
11
+ background-color: #6ca6ea !important;
12
+ }
13
+ ::-webkit-scrollbar-thumb:hover {
14
+ background-color: #355072 !important;
15
+ }</style><style>.notification-item {
16
+ cursor: pointer;
17
+ }
18
+ </style><!-- Page loading styles--><style>.page-loading {
19
+ position: fixed;
20
+ top: 0;
21
+ right: 0;
22
+ bottom: 0;
23
+ left: 0;
24
+ width: 100%;
25
+ height: 100%;
26
+ -webkit-transition: all .4s .2s ease-in-out;
27
+ transition: all .4s .2s ease-in-out;
28
+ background-color: #000;
29
+ opacity: 0;
30
+ visibility: hidden;
31
+ z-index: 9999;
32
+ }
33
+ .page-loading.active {
34
+ opacity: 1;
35
+ visibility: visible;
36
+ }
37
+ .page-loading-inner {
38
+ position: absolute;
39
+ top: 50%;
40
+ left: 0;
41
+ width: 100%;
42
+ text-align: center;
43
+ -webkit-transform: translateY(-50%);
44
+ transform: translateY(-50%);
45
+ -webkit-transition: opacity .2s ease-in-out;
46
+ transition: opacity .2s ease-in-out;
47
+ opacity: 0;
48
+ }
49
+ .page-loading.active > .page-loading-inner {
50
+ opacity: 1;
51
+ }
52
+ .page-loading-inner > span {
53
+ display: block;
54
+ font-family: 'Inter', sans-serif;
55
+ font-size: 1rem;
56
+ font-weight: normal;
57
+ color: #737491;
58
+ }
59
+ .page-spinner {
60
+ display: inline-block;
61
+ width: 2.75rem;
62
+ height: 2.75rem;
63
+ margin-bottom: .75rem;
64
+ vertical-align: text-bottom;
65
+ border: .15em solid #6ca6ea;
66
+ border-right-color: transparent;
67
+ border-radius: 50%;
68
+ -webkit-animation: spinner .75s linear infinite;
69
+ animation: spinner .75s linear infinite;
70
+ }
71
+ @-webkit-keyframes spinner {
72
+ 100% {
73
+ -webkit-transform: rotate(360deg);
74
+ transform: rotate(360deg);
75
+ }
76
+ }
77
+ @keyframes spinner {
78
+ 100% {
79
+ -webkit-transform: rotate(360deg);
80
+ transform: rotate(360deg);
81
+ }
82
+ }
83
+ </style><!-- Page loading scripts--><script>(function () {
84
+ window.onload = function () {
85
+ var preloader = document.querySelector('.page-loading');
86
+ preloader.classList.remove('active');
87
+ };
88
+ })();
89
+ </script><!-- Vendor Styles--><link rel="stylesheet" media="screen" href="/vendor/simplebar/dist/simplebar.min.css"/><!-- Custom Styles--><!-- Main Theme Styles + Bootstrap--><link rel="stylesheet" media="screen" href="/css/theme.min.css?v2.2"><link rel="stylesheet" href="/css/etemplate.css?v1.1"><!-- Enhancement layer (loads after theme to override)--><!-- Firebase--><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-app.js" defer data-cfasync="false"></script><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-auth.js" defer data-cfasync="false"></script><script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-firestore.js" defer data-cfasync="false"></script><script src="/js/cookie.js" defer data-cfasync="false"></script><!-- Moment - Deferred to prevent blocking--><script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.3/moment.min.js"></script><!-- Jquery - Conditionally deferred on Event Hub only--><script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script><!-- Notyf--><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css"><script src="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js" defer></script></head><!-- Body--><body style="background-color:#0a0e17;"><!-- Page loading spinner--><div class="page-loading active"><div class="page-loading-inner"><div class="page-spinner"></div><span>Loading...</span></div></div><div class="offcanvas offcanvas-end" id="notificationOffcanvas" tabindex="-1"><div class="offcanvas-header"><h5 class="offcanvas-title">Notifications</h5><button class="btn-close btn-close-white" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body"></div></div><main class="page-wrapper"><!-- Sign In Modal--><div class="modal fade" id="modal-signin" tabindex="-1"><div class="modal-dialog modal-dialog-centered" role="document"><div class="modal-content border-0"><div class="view show" id="modal-signin-view"><div class="modal-header border-0 bg-primary px-4"><h4 class="modal-title text-light">Sign in</h4><button class="btn-close btn-close-white" type="button" data-bs-dismiss="modal" aria-label="btn-close "></button></div><div class="modal-body px-4 bg-dark"><form class="needs-validation" id="LoginForm" novalidate><div class="mb-3"><div class="input-group"><i class="ai-mail position-absolute top-50 start-0 translate-middle-y ms-3"></i><input class="form-control rounded" id="loginEmail" type="email" placeholder="Email" required></div></div><div class="mb-3"><div class="input-group"><i class="ai-lock position-absolute top-50 start-0 translate-middle-y ms-3"></i><div class="password-toggle w-100"><input class="form-control" id="loginPassword" type="password" placeholder="Password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div></div></div><div class="d-flex justify-content-between align-items-center mb-3 mb-3"><div class="form-check"><input class="form-check-input" type="checkbox" id="keep-signed"><label class="form-check-label fs-sm" for="keep-signed">Keep me signed in</label></div><a class="nav-link-style fs-ms" href="/forgot-password">Forgot password?</a></div><button class="btn btn-primary d-block w-100" id="loginSubmit" type="submit">Sign in</button><p class="fs-sm pt-3 mb-0">Don't have an account? <a href='#' class='fw-medium' data-view='#modal-signup-view'>Sign up</a></p></form></div></div><div class="view" id="modal-signup-view"><div class="modal-header border-0 bg-primary px-4"><h4 class="modal-title text-light">Sign up</h4><button class="btn-close btn-close-white" type="button" data-bs-dismiss="modal" aria-label="btn-close"></button></div><div class="modal-body px-4 bg-dark"><p class="fs-ms text-muted">Registration takes less than a minute!</p><form class="needs-validation" id="RegisterForm" novalidate><div class="mb-3"><input class="form-control" id="regName" type="text" placeholder="Full name" required></div><div class="mb-3"><input class="form-control" id="regEmail" type="email" placeholder="Email" required></div><div class="mb-3 password-toggle"><input class="form-control" id="regPass1" type="password" placeholder="Password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div><div class="mb-3 password-toggle"><input class="form-control" id="regPass2" type="password" placeholder="Confirm password" required><label class="password-toggle-btn" aria-label="Show/hide password"><input class="password-toggle-check" type="checkbox"><span class="password-toggle-indicator"></span></label></div><div class="mb-3 form-check"><input class="form-check-input" id="regAgree" type="checkbox" required><label class="form-check-label">I agree to the <a href="/privacy-policy" target="_blank">Privacy Policy </a>and <a href="/terms-and-conditions" target="_blank">Terms and Conditions</a></label></div><div class="mb-3 form-check"><input class="form-check-input" id="regNewsletter" type="checkbox" checked><label class="form-check-label">Subscribe to our <a href="https://newsletter.topdeck.gg/">monthly newsletter</a></label></div><button class="btn btn-primary d-block w-100" id="regSubmit" type="submit">Sign up</button><p class="text-danger fs-sm pt-1 mb-0" id="signup-error" style="display: none"></p><p class="fs-sm pt-3 mb-0">Already have an account? <a href='#' class='fw-medium' data-view='#modal-signin-view'>Sign in</a></p></form></div></div></div></div></div><script src="/js/account/login.js?v1.4"></script><!-- Navbar (Solid background)--><!-- Remove "navbar-sticky" class to make navigation bar scrollable with the page.--><header class="header navbar navbar-expand-lg navbar-light bg-dark navbar-sticky" data-scroll-header data-fixed-element><div class="container px-0 px-xl-3"><button class="navbar-toggler ms-n2 me-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#primaryMenu"><span class="navbar-toggler-icon"></span></button><a class="navbar-brand flex-shrink-0 order-lg-1 mx-auto ms-lg-0 pe-lg-2 me-lg-4" href="/"><img class="d-none d-lg-block" src="/img/logo/TopDeckNoBorder.png" alt="TopDeck.gg" width="58"><img class="d-lg-none" src="/img/logo/TopDeckFullWhiteNS.png" alt="TopDeck.gg" width="150"></a><div class="d-flex align-items-center order-lg-3 ms-lg-auto"><a class="nav-link-style text-nowrap" href="#modal-signin" data-bs-toggle="modal" data-view="#modal-signin-view"><i class="ai-user fs-xl me-2 align-middle"></i>Sign in</a><a class="btn btn-primary ms-grid-gutter d-none d-lg-inline-block" href="#modal-signin" data-bs-toggle="modal" data-view="#modal-signup-view">Sign up</a></div><div class="offcanvas offcanvas-collapse order-lg-2" id="primaryMenu"><div class="offcanvas-header navbar-shadow"><h5 class="mt-1 mb-0">Menu</h5><button class="btn-close btn-close-white lead" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body"><!-- Menu--><ul class="navbar-nav"><li class="nav-item dropdown dropdown-mega"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Events</a><div class="dropdown-menu"><div class="dropdown-column mb-0"><h5 class="dropdown-header">Browse Events</h5><a class="dropdown-item" href="/">All Events</a><a class="dropdown-item" href="/magic-the-gathering">Magic: The Gathering</a><a class="dropdown-item" href="/pokemon">Pokémon</a><a class="dropdown-item" href="/yu-gi-oh">Yu-Gi-Oh!</a><a class="dropdown-item" href="/one-piece">One Piece</a><a class="dropdown-item" href="/gundam">Gundam TCG</a><a class="dropdown-item" href="/star-wars-unlimited">Star Wars Unlimited</a><a class="dropdown-item" href="/disney-lorcana">Disney Lorcana</a></div><div class="dropdown-column mb-0"><h5 class="dropdown-header d-none d-lg-block">&nbsp;</h5><a class="dropdown-item" href="/riftbound">Riftbound</a><a class="dropdown-item" href="/marvel-snap">Marvel Snap</a><a class="dropdown-item" href="/naruto">Naruto Mythos TCG</a><a class="dropdown-item" href="/world-of-kylia">Hadrai'da Ascendant</a><a class="dropdown-item" href="/beyblade">Beyblade</a><a class="dropdown-item" href="/cardfight-vanguard">Cardfight Vanguard</a><a class="dropdown-item" href="/digimon">Digimon</a><a class="dropdown-item" href="/flesh-and-blood">Flesh and Blood</a></div><div class="dropdown-column mb-0"><h5 class="dropdown-header d-none d-lg-block">&nbsp;</h5><a class="dropdown-item" href="/shadowverse-evolve">Shadowverse: Evolve</a><a class="dropdown-item" href="/sorcery-contested-realm">Sorcery: Contested Realm</a><a class="dropdown-item" href="/altered">Altered</a><a class="dropdown-item" href="/rush-of-ikorr">Rush of Ikorr</a><a class="dropdown-item" href="/cookierun-braverse">CookieRun: Braverse</a><a class="dropdown-item" href="/lairen-la-historia">Lairen: La Historia</a><a class="dropdown-item" href="/settlers-of-catan">Settlers of Catan</a><a class="dropdown-item" href="/gudnak">Gudnak</a></div></div></li><li class="nav-item"><a class="nav-link" href="/subscribe">Subscribe</a></li><li class="nav-item"><a class="nav-link" href="/hubs">Find Stores</a></li><li class="nav-item dropdown dropdown-mega"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Leaderboards</a><div class="dropdown-menu"><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">Magic: The Gathering</h5><a class="dropdown-item" href="/elo/magic-the-gathering/edh">EDH</a><a class="dropdown-item" href="/elo/magic-the-gathering/pauper-edh">Pauper EDH</a><a class="dropdown-item" href="/elo/magic-the-gathering/standard">Standard</a><a class="dropdown-item" href="/elo/magic-the-gathering/pioneer">Pioneer</a><a class="dropdown-item" href="/elo/magic-the-gathering/modern">Modern</a><a class="dropdown-item" href="/elo/magic-the-gathering/legacy">Legacy</a><a class="dropdown-item" href="/elo/magic-the-gathering/pauper">Pauper</a><a class="dropdown-item" href="/elo/magic-the-gathering/vintage">Vintage</a><a class="dropdown-item" href="/elo/magic-the-gathering/premodern">Premodern</a><a class="dropdown-item" href="/elo/magic-the-gathering/limited">Limited</a><a class="dropdown-item" href="/elo/magic-the-gathering/canadian-highlander">Canadian Highlander</a><a class="dropdown-item" href="/elo/magic-the-gathering/duel-commander">Duel Commander</a><a class="dropdown-item" href="/elo/magic-the-gathering/tiny-leaders">Tiny Leaders</a><a class="dropdown-item" href="/elo/magic-the-gathering/7pt-highlander">7pt Highlander</a><a class="dropdown-item" href="/championship-series-2026/leaderboard">Championship Series 2026</a></div><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">Star Wars Unlimited</h5><a class="dropdown-item" href="/elo/star-wars-unlimited/premier">Premier</a><a class="dropdown-item" href="/elo/star-wars-unlimited/draft">Draft</a><a class="dropdown-item" href="/elo/star-wars-unlimited/twin-suns">Twin Suns</a><div class="dropdown-divider"></div><h5 class="dropdown-header my-2">Yu-Gi-Oh!</h5><a class="dropdown-item" href="/elo/yu-gi-oh/advanced">Advanced</a><a class="dropdown-item" href="/elo/yu-gi-oh/edison">Edison</a><a class="dropdown-item" href="/elo/yu-gi-oh/goat">Goat</a><a class="dropdown-item" href="/elo/yu-gi-oh/domain">Domain</a><div class="dropdown-divider"></div><h5 class="dropdown-header my-2">Pokémon</h5><a class="dropdown-item" href="/elo/pokemon/standard">Standard</a><a class="dropdown-item" href="/elo/pokemon/expanded">Expanded</a><a class="dropdown-item" href="/elo/pokemon/legacy">Legacy</a></div><div class="dropdown-column mb-2 mb-lg-0"><h5 class="dropdown-header">More Games</h5><a class="dropdown-item" href="/elo/marvel-snap">Marvel Snap</a><a class="dropdown-item" href="/elo/lorcana">Disney Lorcana</a><a class="dropdown-item" href="/elo/one-piece">One Piece</a><a class="dropdown-item" href="/elo/digimon">Digimon</a><a class="dropdown-item" href="/elo/shadowverse-evolve">Shadowverse Evolve</a><a class="dropdown-item" href="/elo/flesh-and-blood">Flesh and Blood</a><a class="dropdown-item" href="/elo/sorcery-contested-realm">Sorcery: Contested Realm</a><a class="dropdown-item" href="/elo/altered/standard">Altered: Standard</a><a class="dropdown-item" href="/elo/altered/draft">Altered: Draft</a><a class="dropdown-item" href="/elo/beyblade/x">Beyblade X</a></div></div></li><li class="nav-item"><a class="nav-link" href="/championship-series-2026">Championship Series</a></li><li class="nav-item"><a class="nav-link" href="https://shop.topdeck.gg/" target="_blank">Merch</a></li><li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">More</a><ul class="dropdown-menu"><li><h6 class="dropdown-header">Community</h6></li><li><a class="dropdown-item" href="https://newsletter.topdeck.gg/" target="_blank"><i class="fa-solid fa-envelope opacity-60 me-2"></i>Newsletter</a></li><li><a class="dropdown-item" href="/blog"><i class="fa-solid fa-newspaper opacity-60 me-2"></i>What's New</a></li><li><a class="dropdown-item" href="https://discord.topdeck.gg/" target="_blank"><i class="fa-brands fa-discord opacity-60 me-2"></i>Discord Bot</a></li><div class="dropdown-divider"></div><li><h6 class="dropdown-header">Resources</h6></li><li><a class="dropdown-item" href="/docs/tournaments-v2"><i class="fa-solid fa-code opacity-60 me-2"></i>API Documentation</a></li><li><a class="dropdown-item" href="/help"><i class="fa-solid fa-circle-question opacity-60 me-2"></i>Help Center</a></li><div class="dropdown-divider"></div><li><h6 class="dropdown-header">Mobile App</h6></li><li><a class="dropdown-item" href="https://apple.co/3A4Z0FK" target="_blank"><i class="fa-brands fa-apple opacity-60 me-2"></i>Download on iOS</a></li><li><a class="dropdown-item" href="https://play.google.com/store/apps/details?id=com.topdeck.app" target="_blank"><i class="fa-brands fa-google-play opacity-60 me-2"></i>Download on Android</a></li></ul></li></ul></div></div></div></header><script>function EndSession(){
90
+ fetch("/endSession", {
91
+ method: "POST",
92
+ headers: {
93
+ "Accept": "application/json",
94
+ "Content-Type": "application/json",
95
+ },
96
+ }).then((ret) => {
97
+ location.reload();
98
+ });
99
+ }</script><!-- Page content--><div class="offcanvas offcanvas-end w-100" id="viewDecklistOffcanvas" tabindex="-1" aria-labelledby="viewDecklistOffcanvasLabel" aria-hidden="true"><div class="offcanvas-header bg-primary"><h5 class="offcanvas-title" id="viewDecklistOffcanvasLabel">Decklist</h5><button class="btn-close btn-close-white" type="button" data-bs-dismiss="offcanvas" aria-label="Close"></button></div><div class="offcanvas-body bg-dark"><pre class="mb-3"><code class="language-json" id="decklistContent"></code></pre></div></div><script src="/js/supportedGames?t=1770910785740"></script><script>function viewTextDecklist(decklist, title, tournamentId = null, playerId = null, game = null) {
100
+ // Use dynamically served games list
101
+ const supportedGames = window.SUPPORTED_GAMES;
102
+
103
+ // Check if we should use the new deck page
104
+ if (game && supportedGames.has(game) && tournamentId && playerId) {
105
+ // Open the new deck page
106
+ const deckUrl = `/deck/${tournamentId}/${playerId}`;
107
+ window.open(deckUrl, '_blank');
108
+ return;
109
+ }
110
+
111
+ // Handle different decklist formats for offcanvas fallback
112
+ let formattedDecklist;
113
+ if (typeof decklist === 'string') {
114
+ // Try the more comprehensive unescaping first if available
115
+ if (typeof unescapeDecklistFromStorage === 'function') {
116
+ formattedDecklist = unescapeDecklistFromStorage(decklist);
117
+ } else {
118
+ // Fallback to simple newline replacement
119
+ formattedDecklist = decklist.replace(/\\n/g, '\n');
120
+ }
121
+ } else {
122
+ formattedDecklist = String(decklist);
123
+ }
124
+
125
+ // Set content and title
126
+ document.getElementById('decklistContent').textContent = formattedDecklist;
127
+ document.getElementById('viewDecklistOffcanvasLabel').textContent = title + ' - Decklist';
128
+
129
+ // Show the offcanvas
130
+ new bootstrap.Offcanvas(document.getElementById('viewDecklistOffcanvas')).show();
131
+ }
132
+ </script><style>@media (min-width: 768px) {
133
+ #viewDecklistOffcanvas {
134
+ width: 580px !important;
135
+ }
136
+ }
137
+
138
+ @media (min-width: 1200px) {
139
+ #viewDecklistOffcanvas {
140
+ width: 660px !important;
141
+ }
142
+ }</style><!-- Hero Section with Cover Photo--><div class="position-relative overflow-hidden"><div class="position-relative" style="height: 280px; background-image: url(https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/aa6d70ad-de73-4c09-a4d7-1e64d5ed6d00/cover); background-size: cover; background-position: center;"><div class="position-absolute top-0 start-0 w-100 h-100 bg-dark" style="opacity: 0.2"></div></div><!-- Decorative wave divider--><div class="position-absolute bottom-0 w-100"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 100" preserveAspectRatio="none" style="height: 50px; width: 100%;"><path fill="#0a0e17" d="M0,32L60,42.7C120,53,240,75,360,74.7C480,75,600,53,720,42.7C840,32,960,32,1080,37.3C1200,43,1320,53,1380,58.7L1440,64L1440,100L1380,100C1320,100,1200,100,1080,100C960,100,840,100,720,100C600,100,480,100,360,100C240,100,120,100,60,100L0,100Z"></path></svg></div></div><!-- Profile Card--><div class="container position-relative"><div class="card bg-dark text-white shadow-lg border-0 rounded-4 mt-n5 position-relative" style="z-index: 2;"><div class="card-body py-4 px-md-4"><div class="row align-items-start"><!-- Avatar Column--><div class="col-lg-2 col-md-3 text-center text-md-start mb-4 mb-md-0"><div class="position-relative mx-auto" style="width: fit-content;"><img class="rounded-circle shadow-lg" src="https://imagedelivery.net/kN_u_RUfFF6xsGMKYWhO1g/2a7b8d12-5924-4a58-5f9c-c0bf55766800/square" alt="Profile Picture" style="width: 140px; height: 140px; object-fit: cover; margin-top: -70px; border: 4px solid #0a0e17; background-color: #0a0e17;"></div></div><!-- Player Info & About--><div class="col-lg-5 col-md-9 my-sm-auto mb-4"><h2 class="text-white fw-bold mb-1">Björn Kimminich</h2><div class="d-flex flex-wrap mt-2 gap-2"><a class="btn btn-social bs-outline bs-twitter bs-dark me-2" href="https://twitter.com/bkimminich" target="_blank"><i class="fa-brands fa-twitter"></i></a></div><div class="d-flex flex-wrap gap-2 mt-3" id="badgeContainer"></div></div><!-- Stats Card--><div class="col-lg-5 mx-auto"><div class="card bg-dark border-0 shadow-lg rounded-4 position-relative overflow-hidden"><!-- Decorative gradient background--><div class="position-absolute top-0 start-0 w-100 h-100" style="background: linear-gradient(45deg, rgba(90, 79, 158, 0.2), rgba(0, 0, 0, 0)); opacity: 0.7; z-index: 0;"></div><div class="card-body position-relative p-4" style="z-index: 1;"><h4 class="text-white fw-bold mb-4" id="statsTitle">Player Stats</h4><div class="row g-3"><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-trophy text-warning fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="totalTournaments">0</h5><p class="text-light small mb-0">Tournaments</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-chart-line text-success fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="overallRecord">0-0-0</h5><p class="text-light small mb-0">Record</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-percentage text-info fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="overallWinRate">0%</h5><p class="text-light small mb-0">Win Rate</p></div></div></div><div class="col-6"><div class="d-flex align-items-center"><i class="fa-solid fa-bolt text-danger fs-3 me-3"></i><div><h5 class="text-white mb-0 fw-bold" id="conversionRate">0%</h5><p class="text-light small mb-0">Conversion</p></div></div></div></div></div></div></div></div></div></div><!-- Modern Pill Tabs--><div class="card bg-dark border-0 shadow-sm rounded-4 mt-4 p-3"><div class="card-body p-0"><ul class="nav nav-pills gap-2 mb-0" role="tablist"><li class="nav-item"><a class="nav-link active py-3 rounded-3" href="#history" data-bs-toggle="tab"><i class="fa-solid fa-trophy me-2"></i>Tournaments</a></li></ul></div></div><!-- Content tabs--><div class="tab-content mt-4 mb-5"><!-- Tournament History Tab--><div class="tab-pane fade show active" id="history"><div class="card bg-dark border-0 rounded-4 shadow-lg py-4 px-3 px-sm-4"><!-- Filters Section--><div class="d-flex flex-wrap gap-3 mb-4 align-items-center"><!-- Format Dropdown - Modern style--><div class="dropdown"><button class="btn btn-outline-secondary dropdown-toggle" id="formatDropdown" type="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-layer-group me-2"></i><span id="formatDropdownLabel">All Formats</span></button><ul class="dropdown-menu border-0 shadow"><li><a class="dropdown-item" href="#" data-value="all">All Formats</a></li></ul></div><!-- Year Dropdown - Modern style--><div class="dropdown"><button class="btn btn-outline-secondary dropdown-toggle" id="yearDropdown" type="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-calendar-alt me-2"></i><span id="yearDropdownLabel">All Years</span></button><ul class="dropdown-menu border-0 shadow"><li><a class="dropdown-item" href="#" data-value="all">All Years</a></li></ul></div></div><h3 class="card-title text-white fw-bold"><i class="fa-solid fa-history me-2 text-info"></i>Tournament History</h3><div class="tournament-list" id="tournamentList"></div></div></div></div></div></main><footer class="footer bg-black py-5"><div class="container pt-md-2 pt-lg-3"><div class="row pt-sm-2"><!-- Logo, description, and social column--><div class="col-md-4 col-lg-3 pb-2 pb-md-0 mb-4 mb-md-0"><a class="d-block mb-3 mb-md-4" href="/"><img class="img-fluid" src="/img/logo/TopDeckFullWhiteNS.png" alt="TopDeck.gg" style="max-width: 200px;"></a><p class="text-muted pb-2 pb-md-3 mb-3 fs-sm">Discover, organize, and compete in trading card game events. Connect with players worldwide and elevate your game.</p><div class="d-flex gap-1"><a class="btn-social bs-outline bs-light bs-facebook bs-lg me-2" href="https://discord.gg/RjS3mDf3wt" target="_blank"><i class="fa-brands fa-discord"></i></a><a class="btn-social bs-outline bs-light bs-youtube bs-lg me-2" href="https://www.youtube.com/@TopDeckGG_" target="_blank"><i class="fa-brands fa-youtube"></i></a></div></div><!-- Nested columns for links--><div class="col-md-8 col-lg-7 offset-lg-2"><div class="row row-cols-1 row-cols-sm-3"><!-- Company column--><div class="col mb-4 mb-md-0"><div class="widget widget-light"><h4 class="widget-title mb-3">Company</h4><ul><li><a class="widget-link" href="/subscribe">Run a Tournament</a></li><li><a class="widget-link" href="https://newsletter.topdeck.gg/">Newsletter</a></li><li><a class="widget-link" href="/docs/tournaments-v2">API Documentation</a></li><li><a class="widget-link" href="/championship-series-2026">Championship Series</a></li></ul></div></div><!-- Support column--><div class="col mb-4 mb-md-0"><div class="widget widget-light"><h4 class="widget-title mb-3">Support</h4><ul><li><a class="widget-link" href="/cdn-cgi/l/email-protection#30535f5e4451534470445f405455535b1e5757">Contact Us</a></li><li><a class="widget-link" href="/cdn-cgi/l/email-protection#3d505859545c7d49524d59585e56135a5a">Media Inquiries</a></li><li><a class="widget-link" href="/privacy-policy" target="_blank">Privacy Policy</a></li><li><a class="widget-link" href="/terms-and-conditions" target="_blank">Terms & Conditions</a></li></ul></div></div><!-- Install App column--><div class="col"><h4 class="h6 fw-bold pb-2 mb-0 mb-lg-1">Install App</h4><a class="btn btn-outline-secondary d-inline-flex align-items-center px-3 py-2 mt-3 me-3 me-md-0" href="https://apple.co/3A4Z0FK" role="button" style="min-width: 160px;"><i class="fa-brands fa-apple fs-xl me-2"></i><span class="d-flex flex-column align-items-start"><small class="fs-xs text-white-50">Download on the</small><span class="fs-sm">App Store</span></span></a><a class="btn btn-outline-secondary d-inline-flex align-items-center px-3 py-2 mt-3 me-3 me-md-0" href="https://play.google.com/store/apps/details?id=com.topdeck.app" role="button" style="min-width: 160px;"><i class="fa-brands fa-google-play fs-xl me-2"></i><span class="d-flex flex-column align-items-start"><small class="fs-xs text-white-50">Get it on</small><span class="fs-sm">Google Play</span></span></a></div></div></div></div></div></footer><script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>document.addEventListener('DOMContentLoaded', function () {
143
+ // Template variables for decklist viewing
144
+ const playerUsername = "@k0shiii";
145
+ const playerId = "m4VSTJShiXR1PCSCWaM9TBY0rcg1";
146
+
147
+ // Initialize UI with loading states
148
+ let statsTitle = document.getElementById('statsTitle');
149
+ const badgeContainer = document.getElementById('badgeContainer');
150
+ const tournamentList = document.getElementById('tournamentList');
151
+
152
+ // Add loading indicators using Bootstrap 5
153
+ document.getElementById('statsTitle').parentElement.classList.add('position-relative');
154
+ document.getElementById('statsTitle').parentElement.innerHTML += `
155
+ <div class="position-absolute top-0 start-0 w-100 h-100 d-flex justify-content-center align-items-center bg-dark bg-opacity-75" style="z-index: 10; border-radius: 0.5rem;">
156
+ <div class="spinner-border text-primary" role="status">
157
+ <span class="visually-hidden">Loading...</span>
158
+ </div>
159
+ </div>
160
+ `;
161
+
162
+ tournamentList.innerHTML = `
163
+ <div class="text-center py-5">
164
+ <div class="spinner-border text-primary mb-3" role="status">
165
+ <span class="visually-hidden">Loading...</span>
166
+ </div>
167
+ <p class="text-muted mb-0">Loading tournament data...</p>
168
+ </div>
169
+ `;
170
+
171
+ // Store these as global variables to be used after data load
172
+ let gameFormats = {};
173
+ let topFinishes = {};
174
+ let yearlyStats = {};
175
+ let tdcsData = null;
176
+ let leaguesData = null;
177
+
178
+ let selectedFormat = 'all';
179
+ let selectedYear = 'all';
180
+
181
+ // Fetch player data from API
182
+ fetchPlayerData(playerId);
183
+
184
+ // API function to get player data
185
+ async function fetchPlayerData(playerId) {
186
+ try {
187
+ const response = await fetch(`/profile/${playerId}/stats`);
188
+ if (!response.ok) {
189
+ throw new Error('Failed to fetch player data');
190
+ }
191
+
192
+ const data = await response.json();
193
+
194
+ // Store the data globally
195
+ gameFormats = data.gameFormats || {};
196
+ topFinishes = data.topFinishes || {};
197
+ yearlyStats = data.yearlyStats || {};
198
+ tdcsData = data.tdcsData || null;
199
+ leaguesData = data.leaguesData || null;
200
+
201
+ // Remove loading indicators
202
+ const loadingOverlay = document.getElementById('statsTitle').parentElement.querySelector('.position-absolute');
203
+ if (loadingOverlay) {
204
+ loadingOverlay.remove();
205
+ }
206
+
207
+ statsTitle = document.getElementById('statsTitle');
208
+
209
+ // Initialize the UI with the data
210
+ initializeUI();
211
+
212
+ // Populate the UI with data
213
+ updateStatistics();
214
+
215
+ } catch (error) {
216
+ console.error('Error fetching player data:', error);
217
+ tournamentList.innerHTML = `
218
+ <div class="alert alert-danger">
219
+ <i class="fa-solid fa-exclamation-circle me-2"></i>
220
+ Failed to load tournament data. Please try refreshing the page.
221
+ </div>
222
+ `;
223
+ }
224
+ }
225
+
226
+ function initializeUI() {
227
+ // Initialize dropdowns with available formats and years
228
+ populateDropdowns();
229
+
230
+ // Set up event handlers
231
+ setupEventHandlers();
232
+ }
233
+
234
+ function populateDropdowns() {
235
+ // Format dropdown
236
+ const formatDropdown = document.querySelector('#formatDropdown + .dropdown-menu');
237
+ formatDropdown.innerHTML = '<li><a class="dropdown-item" href="#" data-value="all">All Formats</a></li>';
238
+
239
+ Object.keys(gameFormats).forEach(format => {
240
+ const li = document.createElement('li');
241
+ li.innerHTML = `<a class="dropdown-item" href="#" data-value="${format}">${format}</a>`;
242
+ formatDropdown.appendChild(li);
243
+ });
244
+
245
+ // Year dropdown
246
+ const yearDropdown = document.querySelector('#yearDropdown + .dropdown-menu');
247
+ yearDropdown.innerHTML = '<li><a class="dropdown-item" href="#" data-value="all">All Years</a></li>';
248
+
249
+ if (yearlyStats) {
250
+ Object.keys(yearlyStats).sort((a, b) => b - a).forEach(year => {
251
+ const li = document.createElement('li');
252
+ li.innerHTML = `<a class="dropdown-item" href="#" data-value="${year}">${year}</a>`;
253
+ yearDropdown.appendChild(li);
254
+ });
255
+ }
256
+ }
257
+
258
+ function setupEventHandlers() {
259
+ // Dropdown click handlers
260
+ document.querySelectorAll('#formatDropdown + .dropdown-menu a').forEach(item => {
261
+ item.addEventListener('click', e => {
262
+ e.preventDefault();
263
+ selectedFormat = item.getAttribute('data-value');
264
+ document.getElementById('formatDropdownLabel').textContent = item.textContent;
265
+ updateStatistics();
266
+ });
267
+ });
268
+
269
+ document.querySelectorAll('#yearDropdown + .dropdown-menu a').forEach(item => {
270
+ item.addEventListener('click', e => {
271
+ e.preventDefault();
272
+ selectedYear = item.getAttribute('data-value');
273
+ document.getElementById('yearDropdownLabel').textContent = item.textContent;
274
+ updateStatistics();
275
+ });
276
+ });
277
+ }
278
+
279
+ function updateStatistics() {
280
+ updateTitles(selectedFormat, selectedYear);
281
+ updateFormatStats(selectedFormat, selectedYear);
282
+ updateTournamentList(selectedFormat, selectedYear);
283
+ updateLeaguesTab();
284
+ updateTdcsTab();
285
+ }
286
+
287
+ function updateTitles(format, year) {
288
+ const formatText = format === 'all' ? 'Player' : format;
289
+ const yearText = year === 'all' ? '' : ` ${year}`;
290
+ statsTitle.textContent = `${formatText} Stats${yearText}`;
291
+ }
292
+
293
+ function updateFormatStats(format, year) {
294
+ // Clear badges first - this ensures badges are completely reset when changing formats/years
295
+ badgeContainer.innerHTML = '';
296
+
297
+ let stats;
298
+ if (year === 'all') {
299
+ stats = format === 'all' ? calculateOverallStats() : calculateFormatStats(format);
300
+ } else {
301
+ // When a specific year is selected
302
+ if (format === 'all') {
303
+ // All formats for a specific year
304
+ stats = yearlyStats[year]?.overall;
305
+ } else {
306
+ // Specific format for a specific year
307
+ stats = yearlyStats[year]?.[format];
308
+ }
309
+ }
310
+
311
+ if (!stats) {
312
+ // If no stats for the selected format/year combination, show zeros
313
+ document.getElementById('totalTournaments').textContent = '0';
314
+ document.getElementById('overallRecord').textContent = '0-0-0';
315
+ document.getElementById('overallWinRate').textContent = '0.00%';
316
+ document.getElementById('conversionRate').textContent = '0.00%';
317
+ return;
318
+ }
319
+
320
+ document.getElementById('totalTournaments').textContent = stats.totalTournaments;
321
+ document.getElementById('overallRecord').textContent = `${stats.wins}-${stats.losses}-${stats.draws}`;
322
+ const totalGames = stats.wins + stats.losses + stats.draws;
323
+ const winRate = totalGames > 0 ? ((stats.wins / totalGames) * 100).toFixed(2) + '%' : '0.00%';
324
+ document.getElementById('overallWinRate').textContent = winRate;
325
+
326
+ const topFinishesCount = stats.firstPlaceFinishes + stats.top2 + stats.top4 + stats.top8 + stats.top10 + stats.top16;
327
+ const conversionRate = stats.topCutEligible > 0 ? ((topFinishesCount / stats.topCutEligible) * 100).toFixed(2) + '%' : '0.00%';
328
+ document.getElementById('conversionRate').textContent = conversionRate;
329
+
330
+ const createBadge = (label, value, color) => {
331
+ if (value > 0) {
332
+ const badge = document.createElement('span');
333
+ badge.className = 'badge me-1 mb-2';
334
+ badge.style.cssText = `background-color: ${color}; color: #1e1e1e !important;`;
335
+ badge.innerHTML = `<i class="fa-solid fa-medal me-1"></i>${label}: ${value}`;
336
+ badgeContainer.appendChild(badge);
337
+ }
338
+ };
339
+
340
+ // Only create badges if there are stats
341
+ createBadge('Wins', stats.firstPlaceFinishes, '#ffc321');
342
+ createBadge('Finals', stats.top2 + stats.top4, '#C0C0C0');
343
+ createBadge('Top 8', stats.top8, '#f08035');
344
+ createBadge('Top 10', stats.top10, '#3d85c6');
345
+ createBadge('Top 16', stats.top16, '#f08035');
346
+ }
347
+
348
+ function updateTournamentList(format, year) {
349
+ tournamentList.innerHTML = '';
350
+
351
+ // If no gameFormats, show empty state
352
+ if (Object.keys(gameFormats).length === 0) {
353
+ tournamentList.innerHTML = `
354
+ <div class="text-center py-5">
355
+ <p class="text-muted">No tournament data available</p>
356
+ </div>
357
+ `;
358
+ return;
359
+ }
360
+
361
+ // Filter formats based on selection
362
+ const formatsToShow = format === 'all'
363
+ ? Object.entries(gameFormats)
364
+ : [[format, gameFormats[format]]].filter(([_, tournaments]) => tournaments);
365
+
366
+ if (formatsToShow.length === 0) {
367
+ tournamentList.innerHTML = `
368
+ <div class="text-center py-5">
369
+ <p class="text-muted">No tournaments found for the selected criteria</p>
370
+ </div>
371
+ `;
372
+ return;
373
+ }
374
+
375
+ formatsToShow.forEach(([formatName, tournaments]) => {
376
+ // Filter tournaments by year if a specific year is selected
377
+ const filteredTournaments = year === 'all'
378
+ ? tournaments
379
+ : tournaments.filter(t => new Date(t.date).getFullYear().toString() === year);
380
+
381
+ if (filteredTournaments.length > 0) {
382
+ // Format header with a more modern design
383
+ const formatHeader = document.createElement('div');
384
+ formatHeader.className = 'd-flex align-items-center mt-4 mb-3';
385
+ formatHeader.innerHTML = `
386
+ <div class="p-2 rounded-3 me-2" style="background-color: rgba(90, 79, 158, 0.3);">
387
+ <i class="fa-solid fa-dice-d20 text-light"></i>
388
+ </div>
389
+ <h5 class="mb-0 text-white">${formatName}</h5>
390
+ `;
391
+ tournamentList.appendChild(formatHeader);
392
+
393
+ // Create a container for the tournament cards in this format
394
+ const cardContainer = document.createElement('div');
395
+ cardContainer.className = 'row g-3';
396
+ tournamentList.appendChild(cardContainer);
397
+
398
+ filteredTournaments.forEach(tournament => {
399
+ // Get placement for styling
400
+ const placement = parseInt(tournament.placement);
401
+ let placementClass = '';
402
+ let placementIcon = '';
403
+
404
+ if (placement === 1) {
405
+ placementClass = 'border-warning';
406
+ placementIcon = '<i class="fa-solid fa-trophy text-warning me-1"></i>';
407
+ } else if (placement <= 4) {
408
+ placementClass = 'border-info';
409
+ placementIcon = '<i class="fa-solid fa-medal text-info me-1"></i>';
410
+ } else if (placement <= 8) {
411
+ placementClass = 'border-primary';
412
+ placementIcon = '<i class="fa-solid fa-award text-primary me-1"></i>';
413
+ }
414
+
415
+ // Tournament card with enhanced styling
416
+ const tournamentCol = document.createElement('div');
417
+ tournamentCol.className = 'col-12 col-md-6 col-lg-4';
418
+
419
+ const tournamentCard = document.createElement('div');
420
+ tournamentCard.className = `card h-100 bg-secondary text-white shadow-sm ${placementClass ? 'border-start' : ''} ${placementClass}`;
421
+
422
+ // Calculate match percentages for the progress bar
423
+ const [wins, losses, draws] = tournament.record.split('-').map(Number);
424
+ const matches = wins + losses + draws;
425
+ const winPercentage = matches > 0 ? Math.round((wins / matches) * 100) : 0;
426
+ const lossPercentage = matches > 0 ? Math.round((losses / matches) * 100) : 0;
427
+ const drawPercentage = matches > 0 ? Math.round((draws / matches) * 100) : 0;
428
+
429
+ // Format date nicely
430
+ const tournamentDate = new Date(tournament.date);
431
+ const dateOptions = { year: 'numeric', month: 'short', day: 'numeric' };
432
+ const formattedDate = tournamentDate.toLocaleDateString(undefined, dateOptions);
433
+
434
+ tournamentCard.innerHTML = `
435
+ <div class="card-body d-flex flex-column">
436
+ <h6 class="card-title mb-1 text-truncate">${tournament.name}</h6>
437
+ <div class="mb-2">
438
+ <span class="badge bg-dark text-light border border-light">${formattedDate}</span>
439
+ </div>
440
+
441
+ <div class="mt-2 mb-3">
442
+ <div class="d-flex justify-content-between mb-2">
443
+ <div>
444
+ ${placementIcon}
445
+ <span class="text-light">${tournament.placement} of ${tournament.size}</span>
446
+ </div>
447
+ <div class="text-light">
448
+ ${tournament.record}
449
+ </div>
450
+ </div>
451
+
452
+ <div class="progress rounded-pill" style="height: 8px;" title="${wins} wins, ${losses} losses, ${draws} draws">
453
+ ${wins > 0 ? `<div class="progress-bar bg-success" role="progressbar" style="width: ${winPercentage}%" aria-valuenow="${winPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
454
+ ${losses > 0 ? `<div class="progress-bar bg-danger" role="progressbar" style="width: ${lossPercentage}%" aria-valuenow="${lossPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
455
+ ${draws > 0 ? `<div class="progress-bar bg-warning" role="progressbar" style="width: ${drawPercentage}%" aria-valuenow="${drawPercentage}" aria-valuemin="0" aria-valuemax="100"></div>` : ''}
456
+ </div>
457
+ </div>
458
+
459
+ <div class="d-flex flex-wrap gap-2 mt-auto">
460
+ <a href="${tournament.bracketLink}" target="_blank" class="btn btn-sm btn-outline-secondary">
461
+ <i class="fa-solid fa-link me-1"></i>Bracket
462
+ </a>
463
+ ${(tournament.game && window.SUPPORTED_GAMES.has(tournament.game)) ? `
464
+ <button type="button" class="btn btn-sm btn-outline-secondary decklist-btn"
465
+ data-tournament-name="${tournament.name}"
466
+ data-tournament-id="${tournament.id}"
467
+ data-format="${formatName}">
468
+ <i class="fa-solid fa-cards-blank me-1"></i>Decklist
469
+ </button>
470
+ ` : ''}
471
+ </div>
472
+ </div>
473
+ `;
474
+
475
+ tournamentCol.appendChild(tournamentCard);
476
+ cardContainer.appendChild(tournamentCol);
477
+ });
478
+ }
479
+ });
480
+
481
+ // If no tournaments were found after filtering
482
+ if (tournamentList.innerHTML === '') {
483
+ tournamentList.innerHTML = `
484
+ <div class="text-center py-5">
485
+ <p class="text-muted">No tournaments found for the selected criteria</p>
486
+ </div>
487
+ `;
488
+ }
489
+
490
+ // Add event listeners for decklist buttons
491
+ document.querySelectorAll('.decklist-btn').forEach(button => {
492
+ button.addEventListener('click', function() {
493
+ const tournamentName = this.dataset.tournamentName;
494
+ const tournamentId = this.dataset.tournamentId;
495
+ const playerIdentifier = playerUsername || playerId;
496
+
497
+ const format = this.dataset.format;
498
+ const baseGame = format.includes(':') ? format.split(':').slice(0, 2).join(':').trim() : format;
499
+
500
+ viewTextDecklist(null, tournamentName, tournamentId, playerIdentifier, baseGame);
501
+ });
502
+ });
503
+ }
504
+
505
+ function updateTdcsTab() {
506
+ // First, update the tab visibility
507
+ const tabList = document.querySelector('.nav-pills');
508
+ const existingTab = document.querySelector('a[href="#championship"]');
509
+
510
+ if (tdcsData) {
511
+ // Show the tab if TDCS data exists
512
+ if (!existingTab) {
513
+ const tabItem = document.createElement('li');
514
+ tabItem.className = 'nav-item';
515
+ tabItem.innerHTML = `
516
+ <a class="nav-link py-3 rounded-3" href="#championship" data-bs-toggle="tab">
517
+ <i class="fa-solid fa-award me-2"></i>
518
+ Series
519
+ </a>
520
+ `;
521
+ tabList.appendChild(tabItem);
522
+ }
523
+
524
+ // Create or update the tab content
525
+ const tabContent = document.querySelector('.tab-content');
526
+ let championshipPane = document.getElementById('championship');
527
+
528
+ if (!championshipPane) {
529
+ championshipPane = document.createElement('div');
530
+ championshipPane.id = 'championship';
531
+ championshipPane.className = 'tab-pane fade';
532
+ tabContent.appendChild(championshipPane);
533
+ }
534
+
535
+ championshipPane.innerHTML = `
536
+ <div class="card bg-secondary text-white shadow-sm">
537
+ <div class="card-body">
538
+ <h4 class="card-title text-center mb-4">TopDeck Championship Series 2026</h4>
539
+
540
+ <div class="text-center mb-5">
541
+ <p class="fs-6 mb-1">Total Points</p>
542
+ <h2 class="fw-bold text-highlight">${tdcsData.points}</h2>
543
+ </div>
544
+
545
+ ${generateTierContent()}
546
+ </div>
547
+ </div>
548
+ `;
549
+ } else {
550
+ // Remove the tab if no TDCS data
551
+ if (existingTab) {
552
+ existingTab.parentElement.remove();
553
+ }
554
+
555
+ const championshipPane = document.getElementById('championship');
556
+ if (championshipPane) {
557
+ championshipPane.remove();
558
+ }
559
+ }
560
+ }
561
+
562
+ function updateLeaguesTab() {
563
+ const tabList = document.querySelector('.nav-pills');
564
+ const existingTab = document.querySelector('a[href="#leagues"]');
565
+
566
+ if (leaguesData && leaguesData.length > 0) {
567
+ if (!existingTab) {
568
+ const tabItem = document.createElement('li');
569
+ tabItem.className = 'nav-item';
570
+ tabItem.innerHTML = `
571
+ <a class="nav-link py-3 rounded-3" href="#leagues" data-bs-toggle="tab">
572
+ <i class="fa-solid fa-people-group me-2"></i>
573
+ Leagues
574
+ </a>
575
+ `;
576
+ tabList.appendChild(tabItem);
577
+ }
578
+
579
+ const tabContent = document.querySelector('.tab-content');
580
+ let leaguesPane = document.getElementById('leagues');
581
+
582
+ if (!leaguesPane) {
583
+ leaguesPane = document.createElement('div');
584
+ leaguesPane.id = 'leagues';
585
+ leaguesPane.className = 'tab-pane fade';
586
+ tabContent.appendChild(leaguesPane);
587
+ }
588
+
589
+ let cardsHtml = '';
590
+ leaguesData.forEach(league => {
591
+ const placement = league.placementNumber;
592
+ let placementClass = '';
593
+ let placementIcon = '';
594
+
595
+ if (placement === 1) {
596
+ placementClass = 'border-warning';
597
+ placementIcon = '<i class="fa-solid fa-trophy text-warning me-1"></i>';
598
+ } else if (placement <= 4) {
599
+ placementClass = 'border-info';
600
+ placementIcon = '<i class="fa-solid fa-medal text-info me-1"></i>';
601
+ } else if (placement <= 8) {
602
+ placementClass = 'border-primary';
603
+ placementIcon = '<i class="fa-solid fa-award text-primary me-1"></i>';
604
+ }
605
+
606
+ const [wins, losses, draws] = league.record.split('-').map(Number);
607
+ const matches = wins + losses + draws;
608
+ const winPct = matches > 0 ? Math.round((wins / matches) * 100) : 0;
609
+ const lossPct = matches > 0 ? Math.round((losses / matches) * 100) : 0;
610
+ const drawPct = matches > 0 ? Math.round((draws / matches) * 100) : 0;
611
+
612
+ const leagueDate = new Date(league.date);
613
+ const formattedDate = leagueDate.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
614
+ const gameFormat = league.format ? `${league.game}: ${league.format}` : league.game;
615
+
616
+ cardsHtml += `
617
+ <div class="col-12 col-md-6 col-lg-4">
618
+ <div class="card h-100 bg-secondary text-white shadow-sm ${placementClass ? 'border-start' : ''} ${placementClass}">
619
+ <div class="card-body d-flex flex-column">
620
+ <h6 class="card-title mb-1 text-truncate">${league.name}</h6>
621
+ <div class="mb-2">
622
+ <span class="badge bg-dark text-light border border-light">${formattedDate}</span>
623
+ <span class="badge bg-dark text-light border border-light ms-1">${gameFormat}</span>
624
+ </div>
625
+
626
+ <div class="mt-2 mb-3">
627
+ <div class="d-flex justify-content-between mb-2">
628
+ <div>
629
+ ${placementIcon}
630
+ <span class="text-light">${league.placement} of ${league.size}</span>
631
+ </div>
632
+ <div class="text-light">
633
+ ${league.record}
634
+ </div>
635
+ </div>
636
+
637
+ <div class="progress rounded-pill" style="height: 8px;" title="${wins} wins, ${losses} losses, ${draws} draws">
638
+ ${wins > 0 ? `<div class="progress-bar bg-success" role="progressbar" style="width: ${winPct}%"></div>` : ''}
639
+ ${losses > 0 ? `<div class="progress-bar bg-danger" role="progressbar" style="width: ${lossPct}%"></div>` : ''}
640
+ ${draws > 0 ? `<div class="progress-bar bg-warning" role="progressbar" style="width: ${drawPct}%"></div>` : ''}
641
+ </div>
642
+ </div>
643
+
644
+ <div class="d-flex flex-wrap gap-2 mt-auto">
645
+ <a href="${league.bracketLink}" target="_blank" class="btn btn-sm btn-outline-secondary">
646
+ <i class="fa-solid fa-link me-1"></i>Bracket
647
+ </a>
648
+ </div>
649
+ </div>
650
+ </div>
651
+ </div>
652
+ `;
653
+ });
654
+
655
+ leaguesPane.innerHTML = `
656
+ <div class="card bg-dark border-0 rounded-4 shadow-lg py-4 px-3 px-sm-4">
657
+ <h3 class="card-title text-white fw-bold">
658
+ <i class="fa-solid fa-people-group me-2 text-info"></i>
659
+ League History
660
+ </h3>
661
+ <div class="row g-3 mt-2">
662
+ ${cardsHtml}
663
+ </div>
664
+ </div>
665
+ `;
666
+ } else {
667
+ if (existingTab) {
668
+ existingTab.parentElement.remove();
669
+ }
670
+ const leaguesPane = document.getElementById('leagues');
671
+ if (leaguesPane) {
672
+ leaguesPane.remove();
673
+ }
674
+ }
675
+ }
676
+
677
+ function generateTierContent() {
678
+ const tiers = ['Diamond', 'Platinum', 'Gold', 'Silver', 'Bronze'];
679
+ let content = '';
680
+
681
+ tiers.forEach(tier => {
682
+ if (tdcsData.tierBreakdown && tdcsData.tierBreakdown[tier] && tdcsData.tierBreakdown[tier].length > 0) {
683
+ content += `
684
+ <div class="d-flex align-items-center mb-3 mt-5">
685
+ <img src="/img/tdcs/new/TD${tier}.png" alt="${tier} Tier" style="height: 40px; width: auto; margin-right: 12px;">
686
+ <h5 class="mb-0">${tier} Tier</h5>
687
+ </div>
688
+
689
+ <div class="row g-3">
690
+ `;
691
+
692
+ tdcsData.tierBreakdown[tier].forEach(item => {
693
+ if (item.tournamentName && item.points > 0) {
694
+ content += `
695
+ <div class="col-12 col-md-6 col-lg-4">
696
+ <div class="card bg-dark text-white h-100 shadow-sm">
697
+ <div class="card-body d-flex flex-column justify-content-between">
698
+ <div class="mb-3">
699
+ <h6 class="card-title mb-1">${item.tournamentName}</h6>
700
+ <p class="text-muted mb-0 small">(${item.standing} of ${item.totalPlayers})</p>
701
+ </div>
702
+ <div class="d-flex justify-content-between align-items-end mt-auto">
703
+ <span class="fw-bold">${item.points} pts</span>
704
+ </div>
705
+ </div>
706
+ </div>
707
+ </div>
708
+ `;
709
+ }
710
+ });
711
+
712
+ content += '</div>';
713
+ }
714
+ });
715
+
716
+ return content;
717
+ }
718
+
719
+ function calculateOverallStats() {
720
+ let overallStats = {
721
+ totalTournaments: 0, topCutEligible: 0, wins: 0, losses: 0, draws: 0,
722
+ firstPlaceFinishes: 0, top2: 0, top4: 0, top8: 0, top10: 0, top16: 0
723
+ };
724
+
725
+ Object.values(topFinishes).forEach(formatFinishes => {
726
+ Object.keys(overallStats).forEach(key => {
727
+ if (key in formatFinishes) {
728
+ overallStats[key] += formatFinishes[key];
729
+ }
730
+ });
731
+ });
732
+
733
+ Object.values(gameFormats).forEach(tournaments => {
734
+ overallStats.totalTournaments += tournaments.length;
735
+ tournaments.forEach(t => {
736
+ const [wins, losses, draws] = t.record.split('-').map(Number);
737
+ overallStats.wins += wins;
738
+ overallStats.losses += losses;
739
+ overallStats.draws += draws;
740
+ if (t.topCut > 0) {
741
+ overallStats.topCutEligible++;
742
+ }
743
+ });
744
+ });
745
+
746
+ return overallStats;
747
+ }
748
+
749
+ function calculateFormatStats(format) {
750
+ // If the format doesn't exist, return empty stats
751
+ if (!gameFormats[format] || !topFinishes[format]) {
752
+ return {
753
+ totalTournaments: 0, topCutEligible: 0, wins: 0, losses: 0, draws: 0,
754
+ firstPlaceFinishes: 0, top2: 0, top4: 0, top8: 0, top10: 0, top16: 0
755
+ };
756
+ }
757
+
758
+ const formatStats = {
759
+ ...topFinishes[format],
760
+ totalTournaments: gameFormats[format].length,
761
+ topCutEligible: 0,
762
+ wins: 0, losses: 0, draws: 0
763
+ };
764
+
765
+ gameFormats[format].forEach(t => {
766
+ const [wins, losses, draws] = t.record.split('-').map(Number);
767
+ formatStats.wins += wins;
768
+ formatStats.losses += losses;
769
+ formatStats.draws += draws;
770
+ if (t.topCut > 0) {
771
+ formatStats.topCutEligible++;
772
+ }
773
+ });
774
+
775
+ return formatStats;
776
+ }
777
+ });</script><!-- Back to top button--><a class="btn-scroll-top" href="#top" data-scroll data-fixed-element><span class="btn-scroll-top-tooltip text-muted fs-sm me-2">Top</span><i class="btn-scroll-top-icon ai-arrow-up"> </i></a><!-- Vendor scrits: js libraries and plugins--><script src="/vendor/bootstrap/dist/js/bootstrap.bundle.min.js"></script><script src="/vendor/simplebar/dist/simplebar.min.js"></script><script src="/vendor/smooth-scroll/dist/smooth-scroll.polyfills.min.js"></script><!-- Main theme script--><script src="/js/theme.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"version":"2024.11.0","token":"0e3ccdaf832b48f5a3585d27a3a6e921","r":1,"server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
778
+ </body></html>