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.
- package/.github/workflows/ci.yml +29 -3
- package/.github/workflows/update-test-data.yml +33 -0
- package/README.md +16 -11
- package/cli.js +30 -13
- package/package.json +21 -2
- package/scripts/update-test-data.js +29 -0
- package/src/fetchers/melee.js +26 -35
- package/src/fetchers/mtgElo.js +40 -40
- package/src/fetchers/topdeck.js +70 -67
- package/src/fetchers/unityLeague.js +45 -45
- package/src/fetchers/untapped.js +54 -0
- package/src/index.js +75 -42
- package/src/utils/httpClient.js +30 -30
- package/test/data/melee.html +77 -62
- package/test/data/mtgElo.html +8 -8
- package/test/data/topdeck.html +778 -662
- package/test/data/topdeck.json +1 -0
- package/test/data/unityLeague.html +1445 -1340
- package/test/data/untapped.json +104 -0
- package/test/edgeCases.test.js +128 -0
- package/test/melee.test.js +23 -23
- package/test/meleeEdgeCases.test.js +53 -0
- package/test/mtgElo.test.js +21 -21
- package/test/mtgEloEdgeCases.test.js +92 -0
- package/test/playerInfoManager.test.js +312 -0
- package/test/topdeck.test.js +42 -29
- package/test/unityLeague.test.js +24 -24
- package/test/unityLeagueEdgeCases.test.js +123 -0
- package/test/untapped.test.js +58 -0
- package/test/verboseLogging.test.js +215 -0
- package/test/winRatePrecision.test.js +25 -0
- package/.github/workflows/pull-player-data.yml +0 -27
package/test/data/topdeck.html
CHANGED
|
@@ -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
|
|
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"> </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"> </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=
|
|
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#
|
|
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
|
-
|
|
178
|
-
let
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
//
|
|
304
|
-
stats = yearlyStats[year]?.
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
document.getElementById('
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
document.getElementById('
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
badge
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
createBadge('
|
|
342
|
-
createBadge('
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
placementClass = 'border-
|
|
406
|
-
placementIcon = '<i class="fa-solid fa-
|
|
407
|
-
} else if (placement <=
|
|
408
|
-
placementClass = 'border-
|
|
409
|
-
placementIcon = '<i class="fa-solid fa-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
</
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
${tournament.
|
|
446
|
-
</div>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
${tournament.
|
|
461
|
-
<
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
data-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if
|
|
512
|
-
|
|
513
|
-
tabItem
|
|
514
|
-
tabItem.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
championshipPane
|
|
530
|
-
championshipPane.
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
<
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
if
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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"> </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"> </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>
|