Advertisement
Guest User

Geoguessr Advanced Stats V6

a guest
Jan 8th, 2025
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 33.80 KB | Source Code | 0 0
  1. // ==UserScript==
  2. // @name         Geoguessr Advanced Stats V6
  3. // @namespace    http://tampermonkey.net/
  4. // @version      6.0
  5. // @description  Fetches Geoguessr API data to get advanced stats for duel games and displays them on the profile page.
  6. // @author       You
  7. // @match        https://www.geoguessr.com/*/me/profile
  8. // @match        https://www.geoguessr.com/me/profile
  9. // @icon         https://www.geoguessr.com/_next/static/media/favicon.bffdd9d3.png
  10.  
  11. // @grant        GM_xmlhttpRequest
  12. // @grant        GM_addStyle
  13. // @require      https://code.jquery.com/jquery-3.6.0.min.js
  14. // @require      https://unpkg.com/axios@1.6.7/dist/axios.min.js
  15. // @require      https://cdn.jsdelivr.net/npm/progressbar.js@1.1.0/dist/progressbar.min.js
  16. // @require      https://cdn.jsdelivr.net/npm/chart.js@3.8.0/dist/chart.min.js
  17. // @connect      https://game-server.geoguessr.com/
  18. // ==/UserScript==
  19.  
  20. (function() {
  21.     'use strict';
  22.  
  23.     // Constants
  24.     const MAX_SCORE = 5000;
  25.     const API_BASE_URL = 'https://game-server.geoguessr.com/api/duels';
  26.  
  27.     // Variables
  28.     let playerId = '';
  29.  
  30.     // Function to convert JSON to CSV
  31.     function jsonToCsv(json) {
  32.         console.log(json)
  33.         const keys = Object.keys(json[0]);
  34.         const csv = json.map(row =>
  35.             keys.map(key => JSON.stringify(row[key], (_, value) => value === null ? '' : value)).join(',')
  36.         );
  37.  
  38.         return [keys.join(','), ...csv].join('\r\n');
  39.     }
  40.  
  41. // Function to download a CSV file
  42.     function downloadCsv(data, filename) {
  43.         const blob = new Blob([data], { type: 'text/csv;charset=utf-8;' });
  44.         const url = URL.createObjectURL(blob);
  45.         const link = document.createElement("a");
  46.         link.setAttribute("href", url);
  47.         link.setAttribute("download", filename);
  48.         link.style.visibility = 'hidden';
  49.         document.body.appendChild(link);
  50.         link.click();
  51.         document.body.removeChild(link);
  52.     }
  53.  
  54. // Function to extract player round data from duel JSON
  55.     function extractPlayerRoundData(duels, playerId) {
  56.         return duels.flatMap(duel => {
  57.             const player = duel.teams.flatMap(team => team.players).find(p => p.playerId === playerId);
  58.             if (!player) return [];
  59.             return player.guesses.map(guess => {
  60.                 const round = duel.rounds.find(r => r.roundNumber === guess.roundNumber);
  61.                 return {
  62.                     gameId: duel.gameId,
  63.                     roundNumber: guess.roundNumber,
  64.                     lat: guess.lat,
  65.                     lng: guess.lng,
  66.                     distance: guess.distance,
  67.                     score: guess.score,
  68.                     panoramaCountryCode: round.panorama.countryCode,
  69.                     roundStartTime: round.startTime,
  70.                     roundEndTime: round.endTime,
  71.                     roundMultiplier: round.multiplier
  72.                 };
  73.             });
  74.         });
  75.     }
  76.  
  77. // Updated function to handle CSV button click
  78.     async function handleDownloadCsv(session, gameIds) {
  79.         const results = await fetchDuels(session, gameIds.map(game => game.id));
  80.         const playerRoundData = extractPlayerRoundData(results, playerId);
  81.         const csvData = jsonToCsv(playerRoundData);
  82.         downloadCsv(csvData, 'duels_stats.csv');
  83.     }
  84.  
  85. // Initialize CSV download button
  86.     function initCsvDownloadButton(session, gameIds) {
  87.         const button = document.createElement('button');
  88.     button.textContent = 'Download Duel Stats CSV';
  89.     button.style.padding = '10px 20px';
  90.     button.style.fontSize = '16px';
  91.     button.style.color = '#ffffff';
  92.     button.style.backgroundColor = '#1a73e8';
  93.     button.style.border = 'none';
  94.     button.style.borderRadius = '5px';
  95.     button.style.cursor = 'pointer';
  96.     button.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
  97.  
  98.     button.onmouseover = () => {
  99.         button.style.backgroundColor = '#0c5abc';
  100.     };
  101.  
  102.     button.onmouseleave = () => {
  103.         button.style.backgroundColor = '#1a73e8';
  104.     };
  105.  
  106.     button.onclick = () => handleDownloadCsv(session, gameIds);
  107.  
  108.     const container = document.querySelectorAll("[class^='container_content']")[document.querySelectorAll("[class^='container_content']").length - 1];
  109.     container.appendChild(button);
  110.     }
  111.  
  112.     // Inject custom styles
  113.     function addCustomStyles() {
  114.         GM_addStyle(`
  115.             table { width:96%; border-collapse: collapse; color:black;}
  116.             th { border: 1px solid #ddd; padding: 8px; text-align: left; background-color: #f2f2f2; cursor: pointer; }
  117.             td { border: 1px solid #ddd; padding: 8px; text-align: left; background-color: #ffffff; }
  118.             .chart-container { margin: auto; width: 80%; height: 400px; }
  119.         `);
  120.     }
  121.  
  122.     // Utility functions for color interpolation
  123.     function interpolateColor(color1, color2, factor) {
  124.         factor = Math.min(1, Math.max(0, factor));
  125.         const color1Parts = parseColor(color1);
  126.         const color2Parts = parseColor(color2);
  127.  
  128.         return color1Parts.map((part, index) =>
  129.             Math.round(part + (factor * (color2Parts[index] - part)))
  130.         ).reduce((acc, val) => acc + ` ${val}`, 'rgb(').concat(')');
  131.     }
  132.  
  133.     function parseColor(hex) {
  134.         return [
  135.             parseInt(hex.slice(1, 3), 16),
  136.             parseInt(hex.slice(3, 5), 16),
  137.             parseInt(hex.slice(5, 7), 16)
  138.         ];
  139.     }
  140.  
  141.     function getColor(value, min, max) {
  142.         const mid = (max + min) / 2;
  143.         return value < mid ?
  144.             interpolateColor("#cd2941", "#ffffff", (value - min) / (mid - min))
  145.             : interpolateColor("#ffffff", "#29cdb5", (value - mid) / (max - mid));
  146.     }
  147.  
  148.     // Calculate global stats from countryData
  149.     function calculateGlobalStats(countryData) {
  150.         let totalScore = 0;
  151.         let totalAccuracy = 0;
  152.         let totalRounds = 0;
  153.         let totalWins = 0;
  154.  
  155.         Object.values(countryData).forEach(data => {
  156.             totalScore += data.totalScore;
  157.             totalAccuracy += data.totalAccuracy;
  158.             totalRounds += data.totalRounds;
  159.             totalWins += data.wins;
  160.         });
  161.  
  162.         const totalAverageScore = Math.floor(totalScore / totalRounds);
  163.         const overallAccuracy = (totalAccuracy / totalRounds).toFixed(2);
  164.         const totalWinRate = ((totalWins / totalRounds) * 100).toFixed(2);
  165.  
  166.         return { totalAverageScore, overallAccuracy, totalWinRate };
  167.     }
  168.  
  169.     // Render circular gauges for global stats
  170.     function renderGauges(container, globalStats) {
  171.         if (!container) {
  172.             console.error("Container element not found.");
  173.             return;
  174.         }
  175.         const overallStatsWrapper = document.createElement('div');
  176.         overallStatsWrapper.style.marginBottom = '40px';
  177.  
  178.         const titleElement = document.createElement('h2');
  179.         titleElement.textContent = "Overall Stats";
  180.         titleElement.style.color = "#ffa43d";
  181.         titleElement.style.textAlign = 'center';
  182.         titleElement.style.marginBottom = '15px';
  183.  
  184.         overallStatsWrapper.appendChild(titleElement);
  185.  
  186.         const statsWrapper = document.createElement('div');
  187.         statsWrapper.style.display = 'flex';
  188.         statsWrapper.style.justifyContent = 'space-around';
  189.         statsWrapper.style.marginBottom = '20px';
  190.  
  191.         const gaugesData = [
  192.             { label: 'Average Score', value: globalStats.totalAverageScore / MAX_SCORE, actualValue: globalStats.totalAverageScore, maxValue: MAX_SCORE },
  193.             { label: 'Accuracy', value: globalStats.overallAccuracy / 100, actualValue: globalStats.overallAccuracy , maxValue : 100},
  194.             { label: 'Win-Rate', value: globalStats.totalWinRate / 100, actualValue: globalStats.totalWinRate, maxValue : 100 }
  195.         ];
  196.  
  197.         gaugesData.forEach(data => {
  198.             const gaugeWrapper = document.createElement('div');
  199.             gaugeWrapper.style.textAlign = 'center';
  200.  
  201.             if (typeof ProgressBar === 'undefined' || typeof ProgressBar.Circle === 'undefined') {
  202.                 console.error("ProgressBar.js library not found.");
  203.                 return;
  204.             }
  205.             const bar = new ProgressBar.Circle(gaugeWrapper, {
  206.                 trailColor: 'hsla(0,0%,100%,.6)',
  207.                 trailWidth: 15,
  208.                 strokeWidth: 15,
  209.                 text: {
  210.                     value: '',
  211.                     style: {
  212.                         color: '#FFF',
  213.                         position: 'absolute',
  214.                         top: '45%',
  215.                         left: '50%',
  216.                         padding: 0,
  217.                         margin: 0,
  218.                         transform: 'translate(-50%, -50%)',
  219.                     }
  220.                 },
  221.             });
  222.  
  223.             bar.text.style.fontSize = '2rem';
  224.             bar.path.setAttribute('stroke', getColor(data.actualValue, 0, data.maxValue));
  225.             bar.animate(data.value);
  226.             bar.setText(`${data.actualValue}${data.maxValue == 100 ? '%' : ''}`);
  227.  
  228.             const label = document.createElement('div');
  229.             label.style.marginTop = '10px';
  230.             label.style.color = '#ffa43d';
  231.             label.textContent = data.label;
  232.  
  233.             gaugeWrapper.appendChild(label);
  234.             statsWrapper.appendChild(gaugeWrapper);
  235.         });
  236.  
  237.         overallStatsWrapper.appendChild(statsWrapper);
  238.  
  239.         if (overallStatsWrapper.children.length > 0) {
  240.             container.append(overallStatsWrapper);
  241.         }
  242.     }
  243.  
  244.     // Geoguessr API interactions
  245.     async function fetchDuels(session, gameIds) {
  246.         console.log(`Try to fetch ${gameIds.length} games`);
  247.         const results = await Promise.all(gameIds.map(async (gameId) => {
  248.             try {
  249.                 const response = await session.get(`${API_BASE_URL}/${gameId}`);
  250.                 return response.data;
  251.             } catch (error) {
  252.                 console.error(`Failed to fetch data for game ID ${gameId}:`, error);
  253.                 return null;
  254.             }
  255.         }));
  256.         console.log(`Fetched ${results.filter(Boolean).length} games`);
  257.         return results.filter(Boolean);
  258.     }
  259.  
  260.     async function getGameIds(session) {
  261.         const gameIds = [];
  262.         let paginationToken = null;
  263.  
  264.         try {
  265.             while (true) {
  266.                 const response = await session.get('https://www.geoguessr.com/api/v4/feed/private', { params: { paginationToken } });
  267.                 const data = response.data;
  268.                 paginationToken = data.paginationToken;
  269.  
  270.                 if (!playerId && data.entries.length > 0) {
  271.                     playerId = data.entries[0].user.id;
  272.                 }
  273.  
  274.                 data.entries.forEach(entry => {
  275.                     try {
  276.                         extractGameIds(entry, gameIds);
  277.                     } catch (error) {
  278.                         console.error(error);
  279.                     }
  280.                 });
  281.  
  282.                 if (!paginationToken) break;
  283.             }
  284.         } catch (error) {
  285.             console.error("An error occurred while fetching game IDs:", error);
  286.         }
  287.  
  288.         return gameIds;
  289.     }
  290.  
  291.     function extractGameIds(entry, gameIds) {
  292.         const payloadJson = JSON.parse(entry.payload);
  293.         const time = entry.time
  294.         const payloadArray = Array.isArray(payloadJson) ? payloadJson : [payloadJson];
  295.         payloadArray.forEach(payload => {
  296.             // Test if partyId is undefined
  297.             if (payload.gameMode === 'Duels' && payload.partyId === undefined) {
  298.                 gameIds.push({ id: payload.gameId, time: time });
  299.             } else if (payload.payload && payload.payload.gameMode === 'Duels' && payload.payload.partyId === undefined) {
  300.                 gameIds.push({ id: payload.payload.gameId, time: time });
  301.             }
  302.         });
  303.     }
  304.  
  305.     // Table rendering logic
  306.     async function renderTable(session, gameIds) {
  307.         try {
  308.             const dataList = await fetchDuels(session, gameIds.map(game => game.id));
  309.             const weeklyStats = calculateWeeklyStats(dataList, gameIds);
  310.             const countryData = processGameData(dataList);
  311.             const globalStats = calculateGlobalStats(countryData);
  312.  
  313.             const tableData = transformCountryData(countryData);
  314.             const container = document.querySelectorAll("[class^='container_content']")[document.querySelectorAll("[class^='container_content']").length - 1];
  315.             console.log("Global Stats:", globalStats);
  316.             renderGauges(container, globalStats);
  317.             renderHistogram(container, weeklyStats);
  318.             createCountryTable(container, tableData);
  319.         } catch (error) {
  320.             console.error("Error fetching or processing data:", error);
  321.         } finally {
  322.             console.timeEnd('loading_games');
  323.         }
  324.     }
  325.  
  326.     function calculateWeeklyStats(dataList, gameTimeData) {
  327.         const weeklyStats = {};
  328.  
  329.         dataList.forEach((data, index) => {
  330.             const gameTime = new Date(gameTimeData[index].time);
  331.             const weekYear = `${gameTime.getFullYear()}-W${Math.ceil((((gameTime - new Date(gameTime.getFullYear(),0,1)) / 86400000) + gameTime.getDay() + 1)/7)}`;
  332.             if (!weeklyStats[weekYear]) {
  333.                 weeklyStats[weekYear] = { totalAccuracy: 0, totalRounds: 0 };
  334.             }
  335.             const player = data.teams.flatMap(team => team.players).find(player => player.playerId === playerId);
  336.             if (!player) return;
  337.  
  338.             data.rounds.forEach(round => {
  339.                 const playerGuess = player.guesses.find(g => g.roundNumber === round.roundNumber);
  340.                 if (playerGuess) {
  341.                     weeklyStats[weekYear].totalAccuracy += (playerGuess.score * 100) / MAX_SCORE;
  342.                     weeklyStats[weekYear].totalRounds++;
  343.                 }
  344.             });
  345.         });
  346.  
  347.         return Object.keys(weeklyStats).map(week => ({
  348.             week,
  349.             accuracy: (weeklyStats[week].totalAccuracy / weeklyStats[week].totalRounds).toFixed(2)
  350.         }));
  351.     }
  352.  
  353.     function processGameData(dataList) {
  354.         const countryData = {};
  355.  
  356.         dataList.forEach(data => {
  357.             const player = data.teams.flatMap(team => team.players).find(player => player.playerId === playerId);
  358.             if (!player) return;
  359.  
  360.             data.rounds.forEach(round => aggregateRoundData(countryData, round, player, data.teams));
  361.         });
  362.  
  363.         return countryData;
  364.     }
  365.  
  366.     function aggregateRoundData(countryData, round, player, teams) {
  367.         const countryCode = round.panorama.countryCode;
  368.         const playerGuess = player.guesses.find(g => g.roundNumber === round.roundNumber);
  369.  
  370.         if (playerGuess) {
  371.             if (!countryData[countryCode]) {
  372.                 countryData[countryCode] = { totalRounds: 0, totalScore: 0, totalPointDiff: 0, wins: 0, totalAccuracy: 0 };
  373.             }
  374.             let sumPointDiff = 0;
  375.  
  376.             countryData[countryCode].totalRounds++;
  377.             countryData[countryCode].totalScore += playerGuess.score;
  378.             countryData[countryCode].totalAccuracy += (playerGuess.score * 100) / MAX_SCORE;
  379.  
  380.             teams.forEach(team => {
  381.                 team.players.forEach(opponent => {
  382.                     if (opponent.playerId !== playerId) {
  383.                         const opponentGuess = opponent.guesses.find(g => g.roundNumber === round.roundNumber);
  384.                         if (opponentGuess) {
  385.                             let pointDiff = playerGuess.score - opponentGuess.score;
  386.                             sumPointDiff += pointDiff;
  387.                             if (playerGuess.score > opponentGuess.score) {
  388.                                 countryData[countryCode].wins++;
  389.                             }
  390.                         }
  391.                     }
  392.                 });
  393.             });
  394.  
  395.             countryData[countryCode].totalPointDiff += sumPointDiff;
  396.         }
  397.     }
  398.  
  399.     function transformCountryData(countryData) {
  400.         return Object.keys(countryData).map(countryCode => {
  401.             const totalRounds = countryData[countryCode].totalRounds;
  402.             return {
  403.                 country: countryCode,
  404.                 totalRounds: totalRounds,
  405.                 averageScore: (countryData[countryCode].totalScore / totalRounds).toFixed(2),
  406.                 winRate: ((countryData[countryCode].wins / totalRounds) * 100).toFixed(2),
  407.                 totalPointDiff: countryData[countryCode].totalPointDiff,
  408.                 averagePointDiff: (countryData[countryCode].totalPointDiff / totalRounds).toFixed(2),
  409.                 countryAccuracy: (countryData[countryCode].totalAccuracy / totalRounds).toFixed(2)
  410.             };
  411.         });
  412.     }
  413.  
  414.     function createCountryTable(container, tableData) {
  415.         const countryNames = getCountryNames();
  416.         const overallCountryStatsWrapper = document.createElement('div');
  417.         const titleElement = document.createElement('h2');
  418.         titleElement.textContent = "Country Stats";
  419.         titleElement.style.color = "#ffa43d";
  420.         titleElement.style.textAlign = 'center';
  421.         titleElement.style.marginBottom = '15px';
  422.  
  423.         overallCountryStatsWrapper.appendChild(titleElement);
  424.  
  425.         const table = document.createElement('table');
  426.         const thead = document.createElement('thead');
  427.         const headers = ['Country', 'Total Rounds', 'Average Score', 'Win Rate (%)', 'Total Point Diff', 'Average Point Diff', 'Country Accuracy (%)'];
  428.         const headerRow = document.createElement('tr');
  429.  
  430.         headers.forEach((header, index) => {
  431.             const th = document.createElement('th');
  432.             th.textContent = header;
  433.             th.addEventListener('click', () => sortTable(table, index));
  434.             headerRow.appendChild(th);
  435.         });
  436.  
  437.         thead.appendChild(headerRow);
  438.         table.appendChild(thead);
  439.  
  440.         const tbody = document.createElement('tbody');
  441.         tableData.forEach(row => {
  442.             const countryNameWithFlag = countryNames[row.country] || row.country;
  443.             const urlFriendlyName = countryNameWithFlag
  444.                 .replace(/[\u{1F1E6}-\u{1F1FF}]/gu, '')
  445.                 .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
  446.                 .trim().toLowerCase().replace(/ /g, '-');
  447.             const countryLink = `<a href="https://www.plonkit.net/${urlFriendlyName}" target="_blank">${countryNameWithFlag}</a>`;
  448.  
  449.             const tr = document.createElement('tr');
  450.             tr.innerHTML = `
  451.                 <td>${countryLink}</td>
  452.                 <td>${row.totalRounds}</td>
  453.                 <td style="background-color:${getColor(row.averageScore, 0, MAX_SCORE)};">${row.averageScore}</td>
  454.                 <td style="background-color:${getColor(row.winRate, 0, 100)};">${row.winRate}</td>
  455.                 <td style="background-color:${getColor(row.totalPointDiff, -MAX_SCORE, MAX_SCORE)};">${row.totalPointDiff}</td>
  456.                 <td style="background-color:${getColor(row.averagePointDiff, -1000, 1000)};">${row.averagePointDiff}</td>
  457.                 <td style="background-color:${getColor(row.countryAccuracy, 0, 100)};">${row.countryAccuracy}</td>
  458.             `;
  459.             tbody.appendChild(tr);
  460.         });
  461.         table.appendChild(tbody);
  462.  
  463.         overallCountryStatsWrapper.appendChild(table);
  464.         container.appendChild(overallCountryStatsWrapper);
  465.     }
  466.  
  467.     function sortTable(table, columnIndex) {
  468.         const tbody = table.querySelector('tbody');
  469.         const rows = Array.from(tbody.querySelectorAll('tr'));
  470.         const isAsc = table.getAttribute('data-sort-direction') === 'asc';
  471.  
  472.         table.setAttribute('data-sort-direction', isAsc ? 'desc' : 'asc');
  473.  
  474.         rows.sort((a, b) => {
  475.             const aText = a.querySelectorAll('td')[columnIndex].textContent.trim();
  476.             const bText = b.querySelectorAll('td')[columnIndex].textContent.trim();
  477.             return !isNaN(aText) && !isNaN(bText) ?
  478.                 (isAsc ? aText - bText : bText - aText)
  479.                 : (isAsc ? aText.localeCompare(bText) : bText.localeCompare(aText));
  480.         });
  481.  
  482.         rows.forEach(row => tbody.appendChild(row));
  483.     }
  484.  
  485.     function getCountryNames() {
  486.         // Return a mapping object for country codes to country names
  487.         return {
  488.             'ad': 'Andorra ๐Ÿ‡ฆ๐Ÿ‡ฉ',
  489.             'ae': 'United Arab Emirates ๐Ÿ‡ฆ๐Ÿ‡ช',
  490.             'af': 'Afghanistan ๐Ÿ‡ฆ๐Ÿ‡ซ',
  491.             'ag': 'Antigua and Barbuda ๐Ÿ‡ฆ๐Ÿ‡ฌ',
  492.             'ai': 'Anguilla ๐Ÿ‡ฆ๐Ÿ‡ฎ',
  493.             'al': 'Albania ๐Ÿ‡ฆ๐Ÿ‡ฑ',
  494.             'am': 'Armenia ๐Ÿ‡ฆ๐Ÿ‡ฒ',
  495.             'ao': 'Angola ๐Ÿ‡ฆ๐Ÿ‡ด',
  496.             'aq': 'Antarctica ๐Ÿ‡ฆ๐Ÿ‡ถ',
  497.             'ar': 'Argentina ๐Ÿ‡ฆ๐Ÿ‡ท',
  498.             'as': 'American Samoa ๐Ÿ‡ฆ๐Ÿ‡ธ',
  499.             'at': 'Austria ๐Ÿ‡ฆ๐Ÿ‡น',
  500.             'au': 'Australia ๐Ÿ‡ฆ๐Ÿ‡บ',
  501.             'aw': 'Aruba ๐Ÿ‡ฆ๐Ÿ‡ผ',
  502.             'ax': 'ร…land Islands ๐Ÿ‡ฆ๐Ÿ‡ฝ',
  503.             'az': 'Azerbaijan ๐Ÿ‡ฆ๐Ÿ‡ฟ',
  504.             'ba': 'Bosnia and Herzegovina ๐Ÿ‡ง๐Ÿ‡ฆ',
  505.             'bb': 'Barbados ๐Ÿ‡ง๐Ÿ‡ง',
  506.             'bd': 'Bangladesh ๐Ÿ‡ง๐Ÿ‡ฉ',
  507.             'be': 'Belgium ๐Ÿ‡ง๐Ÿ‡ช',
  508.             'bf': 'Burkina Faso ๐Ÿ‡ง๐Ÿ‡ซ',
  509.             'bg': 'Bulgaria ๐Ÿ‡ง๐Ÿ‡ฌ',
  510.             'bh': 'Bahrain ๐Ÿ‡ง๐Ÿ‡ญ',
  511.             'bi': 'Burundi ๐Ÿ‡ง๐Ÿ‡ฎ',
  512.             'bj': 'Benin ๐Ÿ‡ง๐Ÿ‡ฏ',
  513.             'bl': 'Saint Barthรฉlemy ๐Ÿ‡ง๐Ÿ‡ฑ',
  514.             'bm': 'Bermuda ๐Ÿ‡ง๐Ÿ‡ฒ',
  515.             'bn': 'Brunei Darussalam ๐Ÿ‡ง๐Ÿ‡ณ',
  516.             'bo': 'Bolivia ๐Ÿ‡ง๐Ÿ‡ด',
  517.             'bq': 'Bonaire, Sint Eustatius and Saba ๐Ÿ‡ง๐Ÿ‡ถ',
  518.             'br': 'Brazil ๐Ÿ‡ง๐Ÿ‡ท',
  519.             'bs': 'Bahamas ๐Ÿ‡ง๐Ÿ‡ธ',
  520.             'bt': 'Bhutan ๐Ÿ‡ง๐Ÿ‡น',
  521.             'bv': 'Bouvet Island ๐Ÿ‡ง๐Ÿ‡ป',
  522.             'bw': 'Botswana ๐Ÿ‡ง๐Ÿ‡ผ',
  523.             'by': 'Belarus ๐Ÿ‡ง๐Ÿ‡พ',
  524.             'bz': 'Belize ๐Ÿ‡ง๐Ÿ‡ฟ',
  525.             'ca': 'Canada ๐Ÿ‡จ๐Ÿ‡ฆ',
  526.             'cc': 'Cocos (Keeling) Islands ๐Ÿ‡จ๐Ÿ‡จ',
  527.             'cd': 'Congo (Democratic Republic of the) ๐Ÿ‡จ๐Ÿ‡ฉ',
  528.             'cf': 'Central African Republic ๐Ÿ‡จ๐Ÿ‡ซ',
  529.             'cg': 'Congo ๐Ÿ‡จ๐Ÿ‡ฌ',
  530.             'ch': 'Switzerland ๐Ÿ‡จ๐Ÿ‡ญ',
  531.             'ci': 'Cรดte d\'Ivoire ๐Ÿ‡จ๐Ÿ‡ฎ',
  532.             'ck': 'Cook Islands ๐Ÿ‡จ๐Ÿ‡ฐ',
  533.             'cl': 'Chile ๐Ÿ‡จ๐Ÿ‡ฑ',
  534.             'cm': 'Cameroon ๐Ÿ‡จ๐Ÿ‡ฒ',
  535.             'cn': 'China ๐Ÿ‡จ๐Ÿ‡ณ',
  536.             'co': 'Colombia ๐Ÿ‡จ๐Ÿ‡ด',
  537.             'cr': 'Costa Rica ๐Ÿ‡จ๐Ÿ‡ท',
  538.             'cu': 'Cuba ๐Ÿ‡จ๐Ÿ‡บ',
  539.             'cv': 'Cabo Verde ๐Ÿ‡จ๐Ÿ‡ป',
  540.             'cw': 'Curaรงao ๐Ÿ‡จ๐Ÿ‡ผ',
  541.             'cx': 'Christmas Island ๐Ÿ‡จ๐Ÿ‡ฝ',
  542.             'cy': 'Cyprus ๐Ÿ‡จ๐Ÿ‡พ',
  543.             'cz': 'Czechia ๐Ÿ‡จ๐Ÿ‡ฟ',
  544.             'de': 'Germany ๐Ÿ‡ฉ๐Ÿ‡ช',
  545.             'dj': 'Djibouti ๐Ÿ‡ฉ๐Ÿ‡ฏ',
  546.             'dk': 'Denmark ๐Ÿ‡ฉ๐Ÿ‡ฐ',
  547.             'dm': 'Dominica ๐Ÿ‡ฉ๐Ÿ‡ฒ',
  548.             'do': 'Dominican Republic ๐Ÿ‡ฉ๐Ÿ‡ด',
  549.             'dz': 'Algeria ๐Ÿ‡ฉ๐Ÿ‡ฟ',
  550.             'ec': 'Ecuador ๐Ÿ‡ช๐Ÿ‡จ',
  551.             'ee': 'Estonia ๐Ÿ‡ช๐Ÿ‡ช',
  552.             'eg': 'Egypt ๐Ÿ‡ช๐Ÿ‡ฌ',
  553.             'eh': 'Western Sahara ๐Ÿ‡ช๐Ÿ‡ญ',
  554.             'er': 'Eritrea ๐Ÿ‡ช๐Ÿ‡ท',
  555.             'es': 'Spain ๐Ÿ‡ช๐Ÿ‡ธ',
  556.             'et': 'Ethiopia ๐Ÿ‡ช๐Ÿ‡น',
  557.             'fi': 'Finland ๐Ÿ‡ซ๐Ÿ‡ฎ',
  558.             'fj': 'Fiji ๐Ÿ‡ซ๐Ÿ‡ฏ',
  559.             'fk': 'Falkland Islands (Malvinas) ๐Ÿ‡ซ๐Ÿ‡ฐ',
  560.             'fm': 'Micronesia (Federated States of) ๐Ÿ‡ซ๐Ÿ‡ฒ',
  561.             'fo': 'Faroe Islands ๐Ÿ‡ซ๐Ÿ‡ด',
  562.             'fr': 'France ๐Ÿ‡ซ๐Ÿ‡ท',
  563.             'ga': 'Gabon ๐Ÿ‡ฌ๐Ÿ‡ฆ',
  564.             'gb': 'United Kingdom ๐Ÿ‡ฌ๐Ÿ‡ง',
  565.             'gd': 'Grenada ๐Ÿ‡ฌ๐Ÿ‡ฉ',
  566.             'ge': 'Georgia ๐Ÿ‡ฌ๐Ÿ‡ช',
  567.             'gf': 'French Guiana ๐Ÿ‡ฌ๐Ÿ‡ซ',
  568.             'gg': 'Guernsey ๐Ÿ‡ฌ๐Ÿ‡ฌ',
  569.             'gh': 'Ghana ๐Ÿ‡ฌ๐Ÿ‡ญ',
  570.             'gi': 'Gibraltar ๐Ÿ‡ฌ๐Ÿ‡ฎ',
  571.             'gl': 'Greenland ๐Ÿ‡ฌ๐Ÿ‡ฑ',
  572.             'gm': 'Gambia ๐Ÿ‡ฌ๐Ÿ‡ฒ',
  573.             'gn': 'Guinea ๐Ÿ‡ฌ๐Ÿ‡ณ',
  574.             'gp': 'Guadeloupe ๐Ÿ‡ฌ๐Ÿ‡ต',
  575.             'gq': 'Equatorial Guinea ๐Ÿ‡ฌ๐Ÿ‡ถ',
  576.             'gr': 'Greece ๐Ÿ‡ฌ๐Ÿ‡ท',
  577.             'gs': 'South Georgia and the South Sandwich Islands ๐Ÿ‡ฌ๐Ÿ‡ธ',
  578.             'gt': 'Guatemala ๐Ÿ‡ฌ๐Ÿ‡น',
  579.             'gu': 'Guam ๐Ÿ‡ฌ๐Ÿ‡บ',
  580.             'gw': 'Guinea-Bissau ๐Ÿ‡ฌ๐Ÿ‡ผ',
  581.             'gy': 'Guyana ๐Ÿ‡ฌ๐Ÿ‡พ',
  582.             'hk': 'Hong Kong ๐Ÿ‡ญ๐Ÿ‡ฐ',
  583.             'hm': 'Heard Island and McDonald Islands ๐Ÿ‡ญ๐Ÿ‡ฒ',
  584.             'hn': 'Honduras ๐Ÿ‡ญ๐Ÿ‡ณ',
  585.             'hr': 'Croatia ๐Ÿ‡ญ๐Ÿ‡ท',
  586.             'ht': 'Haiti ๐Ÿ‡ญ๐Ÿ‡น',
  587.             'hu': 'Hungary ๐Ÿ‡ญ๐Ÿ‡บ',
  588.             'id': 'Indonesia ๐Ÿ‡ฎ๐Ÿ‡ฉ',
  589.             'ie': 'Ireland ๐Ÿ‡ฎ๐Ÿ‡ช',
  590.             'il': 'Israel ๐Ÿ‡ฎ๐Ÿ‡ฑ',
  591.             'im': 'Isle of Man ๐Ÿ‡ฎ๐Ÿ‡ฒ',
  592.             'in': 'India ๐Ÿ‡ฎ๐Ÿ‡ณ',
  593.             'io': 'British Indian Ocean Territory ๐Ÿ‡ฎ๐Ÿ‡ด',
  594.             'iq': 'Iraq ๐Ÿ‡ฎ๐Ÿ‡ถ',
  595.             'ir': 'Iran ๐Ÿ‡ฎ๐Ÿ‡ท',
  596.             'is': 'Iceland ๐Ÿ‡ฎ๐Ÿ‡ธ',
  597.             'it': 'Italy ๐Ÿ‡ฎ๐Ÿ‡น',
  598.             'je': 'Jersey ๐Ÿ‡ฏ๐Ÿ‡ช',
  599.             'jm': 'Jamaica ๐Ÿ‡ฏ๐Ÿ‡ฒ',
  600.             'jo': 'Jordan ๐Ÿ‡ฏ๐Ÿ‡ด',
  601.             'jp': 'Japan ๐Ÿ‡ฏ๐Ÿ‡ต',
  602.             'ke': 'Kenya ๐Ÿ‡ฐ๐Ÿ‡ช',
  603.             'kg': 'Kyrgyzstan ๐Ÿ‡ฐ๐Ÿ‡ฌ',
  604.             'kh': 'Cambodia ๐Ÿ‡ฐ๐Ÿ‡ญ',
  605.             'ki': 'Kiribati ๐Ÿ‡ฐ๐Ÿ‡ฎ',
  606.             'km': 'Comoros ๐Ÿ‡ฐ๐Ÿ‡ฒ',
  607.             'kn': 'Saint Kitts and Nevis ๐Ÿ‡ฐ๐Ÿ‡ณ',
  608.             'kp': 'North Korea ๐Ÿ‡ฐ๐Ÿ‡ต',
  609.             'kr': 'South Korea ๐Ÿ‡ฐ๐Ÿ‡ท',
  610.             'kw': 'Kuwait ๐Ÿ‡ฐ๐Ÿ‡ผ',
  611.             'ky': 'Cayman Islands ๐Ÿ‡ฐ๐Ÿ‡พ',
  612.             'kz': 'Kazakhstan ๐Ÿ‡ฐ๐Ÿ‡ฟ',
  613.             'la': 'Laos ๐Ÿ‡ฑ๐Ÿ‡ฆ',
  614.             'lb': 'Lebanon ๐Ÿ‡ฑ๐Ÿ‡ง',
  615.             'lc': 'Saint Lucia ๐Ÿ‡ฑ๐Ÿ‡จ',
  616.             'li': 'Liechtenstein ๐Ÿ‡ฑ๐Ÿ‡ฎ',
  617.             'lk': 'Sri Lanka ๐Ÿ‡ฑ๐Ÿ‡ฐ',
  618.             'lr': 'Liberia ๐Ÿ‡ฑ๐Ÿ‡ท',
  619.             'ls': 'Lesotho ๐Ÿ‡ฑ๐Ÿ‡ธ',
  620.             'lt': 'Lithuania ๐Ÿ‡ฑ๐Ÿ‡น',
  621.             'lu': 'Luxembourg ๐Ÿ‡ฑ๐Ÿ‡บ',
  622.             'lv': 'Latvia ๐Ÿ‡ฑ๐Ÿ‡ป',
  623.             'ly': 'Libya ๐Ÿ‡ฑ๐Ÿ‡พ',
  624.             'ma': 'Morocco ๐Ÿ‡ฒ๐Ÿ‡ฆ',
  625.             'mc': 'Monaco ๐Ÿ‡ฒ๐Ÿ‡จ',
  626.             'md': 'Moldova ๐Ÿ‡ฒ๐Ÿ‡ฉ',
  627.             'me': 'Montenegro ๐Ÿ‡ฒ๐Ÿ‡ช',
  628.             'mf': 'Saint Martin ๐Ÿ‡ฒ๐Ÿ‡ซ',
  629.             'mg': 'Madagascar ๐Ÿ‡ฒ๐Ÿ‡ฌ',
  630.             'mh': 'Marshall Islands ๐Ÿ‡ฒ๐Ÿ‡ญ',
  631.             'mk': 'North Macedonia ๐Ÿ‡ฒ๐Ÿ‡ฐ',
  632.             'ml': 'Mali ๐Ÿ‡ฒ๐Ÿ‡ฑ',
  633.             'mm': 'Myanmar ๐Ÿ‡ฒ๐Ÿ‡ฒ',
  634.             'mn': 'Mongolia ๐Ÿ‡ฒ๐Ÿ‡ณ',
  635.             'mo': 'Macao ๐Ÿ‡ฒ๐Ÿ‡ด',
  636.             'mp': 'Northern Mariana Islands ๐Ÿ‡ฒ๐Ÿ‡ต',
  637.             'mq': 'Martinique ๐Ÿ‡ฒ๐Ÿ‡ถ',
  638.             'mr': 'Mauritania ๐Ÿ‡ฒ๐Ÿ‡ท',
  639.             'ms': 'Montserrat ๐Ÿ‡ฒ๐Ÿ‡ธ',
  640.             'mt': 'Malta ๐Ÿ‡ฒ๐Ÿ‡น',
  641.             'mu': 'Mauritius ๐Ÿ‡ฒ๐Ÿ‡บ',
  642.             'mv': 'Maldives ๐Ÿ‡ฒ๐Ÿ‡ป',
  643.             'mw': 'Malawi ๐Ÿ‡ฒ๐Ÿ‡ผ',
  644.             'mx': 'Mexico ๐Ÿ‡ฒ๐Ÿ‡ฝ',
  645.             'my': 'Malaysia ๐Ÿ‡ฒ๐Ÿ‡พ',
  646.             'mz': 'Mozambique ๐Ÿ‡ฒ๐Ÿ‡ฟ',
  647.             'na': 'Namibia ๐Ÿ‡ณ๐Ÿ‡ฆ',
  648.             'nc': 'New Caledonia ๐Ÿ‡ณ๐Ÿ‡จ',
  649.             'ne': 'Niger ๐Ÿ‡ณ๐Ÿ‡ช',
  650.             'nf': 'Norfolk Island ๐Ÿ‡ณ๐Ÿ‡ซ',
  651.             'ng': 'Nigeria ๐Ÿ‡ณ๐Ÿ‡ฌ',
  652.             'ni': 'Nicaragua ๐Ÿ‡ณ๐Ÿ‡ฎ',
  653.             'nl': 'Netherlands ๐Ÿ‡ณ๐Ÿ‡ฑ',
  654.             'no': 'Norway ๐Ÿ‡ณ๐Ÿ‡ด',
  655.             'np': 'Nepal ๐Ÿ‡ณ๐Ÿ‡ต',
  656.             'nr': 'Nauru ๐Ÿ‡ณ๐Ÿ‡ท',
  657.             'nu': 'Niue ๐Ÿ‡ณ๐Ÿ‡บ',
  658.             'nz': 'New Zealand ๐Ÿ‡ณ๐Ÿ‡ฟ',
  659.             'om': 'Oman ๐Ÿ‡ด๐Ÿ‡ฒ',
  660.             'pa': 'Panama ๐Ÿ‡ต๐Ÿ‡ฆ',
  661.             'pe': 'Peru ๐Ÿ‡ต๐Ÿ‡ช',
  662.             'pf': 'French Polynesia ๐Ÿ‡ต๐Ÿ‡ซ',
  663.             'pg': 'Papua New Guinea ๐Ÿ‡ต๐Ÿ‡ฌ',
  664.             'ph': 'Philippines ๐Ÿ‡ต๐Ÿ‡ญ',
  665.             'pk': 'Pakistan ๐Ÿ‡ต๐Ÿ‡ฐ',
  666.             'pl': 'Poland ๐Ÿ‡ต๐Ÿ‡ฑ',
  667.             'pm': 'Saint Pierre and Miquelon ๐Ÿ‡ต๐Ÿ‡ฒ',
  668.             'pn': 'Pitcairn ๐Ÿ‡ต๐Ÿ‡ณ',
  669.             'pr': 'Puerto Rico ๐Ÿ‡ต๐Ÿ‡ท',
  670.             'ps': 'Palestine ๐Ÿ‡ต๐Ÿ‡ธ',
  671.             'pt': 'Portugal ๐Ÿ‡ต๐Ÿ‡น',
  672.             'pw': 'Palau ๐Ÿ‡ต๐Ÿ‡ผ',
  673.             'py': 'Paraguay ๐Ÿ‡ต๐Ÿ‡พ',
  674.             'qa': 'Qatar ๐Ÿ‡ถ๐Ÿ‡ฆ',
  675.             're': 'Rรฉunion ๐Ÿ‡ท๐Ÿ‡ช',
  676.             'ro': 'Romania ๐Ÿ‡ท๐Ÿ‡ด',
  677.             'rs': 'Serbia ๐Ÿ‡ท๐Ÿ‡ธ',
  678.             'ru': 'Russia ๐Ÿ‡ท๐Ÿ‡บ',
  679.             'rw': 'Rwanda ๐Ÿ‡ท๐Ÿ‡ผ',
  680.             'sa': 'Saudi Arabia ๐Ÿ‡ธ๐Ÿ‡ฆ',
  681.             'sb': 'Solomon Islands ๐Ÿ‡ธ๐Ÿ‡ง',
  682.             'sc': 'Seychelles ๐Ÿ‡ธ๐Ÿ‡จ',
  683.             'sd': 'Sudan ๐Ÿ‡ธ๐Ÿ‡ฉ',
  684.             'se': 'Sweden ๐Ÿ‡ธ๐Ÿ‡ช',
  685.             'sg': 'Singapore ๐Ÿ‡ธ๐Ÿ‡ฌ',
  686.             'sh': 'Saint Helena ๐Ÿ‡ธ๐Ÿ‡ญ',
  687.             'si': 'Slovenia ๐Ÿ‡ธ๐Ÿ‡ฎ',
  688.             'sj': 'Svalbard and Jan Mayen ๐Ÿ‡ธ๐Ÿ‡ฏ',
  689.             'sk': 'Slovakia ๐Ÿ‡ธ๐Ÿ‡ฐ',
  690.             'sl': 'Sierra Leone ๐Ÿ‡ธ๐Ÿ‡ฑ',
  691.             'sm': 'San Marino ๐Ÿ‡ธ๐Ÿ‡ฒ',
  692.             'sn': 'Senegal ๐Ÿ‡ธ๐Ÿ‡ณ',
  693.             'so': 'Somalia ๐Ÿ‡ธ๐Ÿ‡ด',
  694.             'sr': 'Suriname ๐Ÿ‡ธ๐Ÿ‡ท',
  695.             'ss': 'South Sudan ๐Ÿ‡ธ๐Ÿ‡ธ',
  696.             'st': 'Sao Tome and Principe ๐Ÿ‡ธ๐Ÿ‡น',
  697.             'sv': 'El Salvador ๐Ÿ‡ธ๐Ÿ‡ป',
  698.             'sx': 'Sint Maarten ๐Ÿ‡ธ๐Ÿ‡ฝ',
  699.             'sy': 'Syria ๐Ÿ‡ธ๐Ÿ‡พ',
  700.             'sz': 'Eswatini ๐Ÿ‡ธ๐Ÿ‡ฟ',
  701.             'tc': 'Turks and Caicos Islands ๐Ÿ‡น๐Ÿ‡จ',
  702.             'td': 'Chad ๐Ÿ‡น๐Ÿ‡ฉ',
  703.             'tf': 'French Southern Territories ๐Ÿ‡น๐Ÿ‡ซ',
  704.             'tg': 'Togo ๐Ÿ‡น๐Ÿ‡ฌ',
  705.             'th': 'Thailand ๐Ÿ‡น๐Ÿ‡ญ',
  706.             'tj': 'Tajikistan ๐Ÿ‡น๐Ÿ‡ฏ',
  707.             'tk': 'Tokelau ๐Ÿ‡น๐Ÿ‡ฐ',
  708.             'tl': 'Timor-Leste ๐Ÿ‡น๐Ÿ‡ฑ',
  709.             'tm': 'Turkmenistan ๐Ÿ‡น๐Ÿ‡ฒ',
  710.             'tn': 'Tunisia ๐Ÿ‡น๐Ÿ‡ณ',
  711.             'to': 'Tonga ๐Ÿ‡น๐Ÿ‡ด',
  712.             'tr': 'Turkey ๐Ÿ‡น๐Ÿ‡ท',
  713.             'tt': 'Trinidad and Tobago ๐Ÿ‡น๐Ÿ‡น',
  714.             'tv': 'Tuvalu ๐Ÿ‡น๐Ÿ‡ป',
  715.             'tw': 'Taiwan ๐Ÿ‡น๐Ÿ‡ผ',
  716.             'tz': 'Tanzania ๐Ÿ‡น๐Ÿ‡ฟ',
  717.             'ua': 'Ukraine ๐Ÿ‡บ๐Ÿ‡ฆ',
  718.             'ug': 'Uganda ๐Ÿ‡บ๐Ÿ‡ฌ',
  719.             'um': 'United States Minor Outlying Islands ๐Ÿ‡บ๐Ÿ‡ฒ',
  720.             'us': 'United States ๐Ÿ‡บ๐Ÿ‡ธ',
  721.             'uy': 'Uruguay ๐Ÿ‡บ๐Ÿ‡พ',
  722.             'uz': 'Uzbekistan ๐Ÿ‡บ๐Ÿ‡ฟ',
  723.             'va': 'Vatican City ๐Ÿ‡ป๐Ÿ‡ฆ',
  724.             'vc': 'Saint Vincent and the Grenadines ๐Ÿ‡ป๐Ÿ‡จ',
  725.             've': 'Venezuela ๐Ÿ‡ป๐Ÿ‡ช',
  726.             'vg': 'British Virgin Islands ๐Ÿ‡ป๐Ÿ‡ฌ',
  727.             'vi': 'U.S. Virgin Islands ๐Ÿ‡ป๐Ÿ‡ฎ',
  728.             'vn': 'Vietnam ๐Ÿ‡ป๐Ÿ‡ณ',
  729.             'vu': 'Vanuatu ๐Ÿ‡ป๐Ÿ‡บ',
  730.             'wf': 'Wallis and Futuna ๐Ÿ‡ผ๐Ÿ‡ซ',
  731.             'ws': 'Samoa ๐Ÿ‡ผ๐Ÿ‡ธ',
  732.             'xk': 'Kosovo ๐Ÿ‡ฝ๐Ÿ‡ฐ',
  733.             'ye': 'Yemen ๐Ÿ‡พ๐Ÿ‡ช',
  734.             'yt': 'Mayotte ๐Ÿ‡พ๐Ÿ‡น',
  735.             'za': 'South Africa ๐Ÿ‡ฟ๐Ÿ‡ฆ',
  736.             'zm': 'Zambia ๐Ÿ‡ฟ๐Ÿ‡ฒ',
  737.             'zw': 'Zimbabwe ๐Ÿ‡ฟ๐Ÿ‡ผ',
  738.         };
  739.     }
  740.  
  741.     // Function to render a histogram showing weekly accuracy
  742.     function renderHistogram(container, weeklyStats) {
  743.         // reverse the array to show the most recent week last
  744.         weeklyStats.reverse();
  745.         const histogramWrapper = document.createElement('div');
  746.         const titleElement = document.createElement('h2');
  747.         titleElement.textContent = "Weekly Accuracy";
  748.         titleElement.style.color = "#ffa43d";
  749.         titleElement.style.textAlign = 'center';
  750.         titleElement.style.marginBottom = '15px';
  751.  
  752.         histogramWrapper.appendChild(titleElement);
  753.  
  754.         const chartCanvas = document.createElement('canvas');
  755.         histogramWrapper.appendChild(chartCanvas);
  756.         histogramWrapper.classList.add('chart-container');
  757.         histogramWrapper.style.marginBottom = '60px';
  758.  
  759.         container.appendChild(histogramWrapper);
  760.  
  761.         const ctx = chartCanvas.getContext('2d');
  762.         const chartData = {
  763.             labels: weeklyStats.map(stat => stat.week),
  764.             datasets: [{
  765.                 label: 'Accuracy (%)',
  766.                 data: weeklyStats.map(stat => stat.accuracy),
  767.                 backgroundColor: 'rgba(41, 205, 181, 0.5)',
  768.                 borderColor: '#29cdb5',
  769.                 borderWidth: 1,
  770.                 fill: true,
  771.                 tension: 0.1
  772.             }]
  773.         };
  774.  
  775.         new Chart(ctx, {
  776.             type: 'line',
  777.             data: chartData,
  778.             options: {
  779.                 responsive: true,
  780.                 maintainAspectRatio: false,
  781.                 plugins: {
  782.                     legend: {
  783.                         position: 'top',
  784.                         labels: {
  785.                             color: '#ffffff',
  786.                         }
  787.                     },
  788.                     title: {
  789.                         display: true,
  790.                         text: 'Weekly Accuracy',
  791.                         color: '#ffa43d'
  792.                     }
  793.                 },
  794.                 scales: {
  795.                     x: {
  796.                         title: {
  797.                             display: true,
  798.                             text: 'Week',
  799.                             color: '#ffffff'
  800.                         },
  801.                         ticks: {
  802.                             color: '#ffffff'
  803.                         }
  804.                     },
  805.                     y: {
  806.                         beginAtZero: true,
  807.                         max: 100,
  808.                         title: {
  809.                             display: true,
  810.                             text: 'Accuracy (%)',
  811.                             color: '#ffffff'
  812.                         },
  813.                         ticks: {
  814.                             color: '#ffffff'
  815.                         }
  816.                     }
  817.                 }
  818.             }
  819.         });
  820.     }
  821.  
  822.     // Initialization
  823.     function init() {
  824.         console.time('loading_games');
  825.         addCustomStyles();
  826.  
  827.         const session = axios.create({
  828.             withCredentials: true
  829.         });
  830.         // gameIds.splice(0,100) to get only your last 100 games. Change the number to get more or less games
  831.         getGameIds(session).then(gameIds => {
  832.             const recentGameIds = gameIds.splice(0, 100);
  833.             renderTable(session, recentGameIds);
  834.             initCsvDownloadButton(session, recentGameIds);
  835.         });
  836.     }
  837.  
  838.     $(document).ready(init);
  839.  
  840. })();
  841.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement