Geoguessr Activities Analysis Tool

map visualization of your geoguessr activities

目前为 2024-05-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         Geoguessr Activities Analysis Tool
// @version      1.1
// @description  map visualization of your geoguessr activities
// @author       KaKa
// @match        https://map-making.app/
// @require      https://cdn.jsdelivr.net/npm/sweetalert2@11
// @license      MIT
// @icon         https://www.google.com/s2/favicons?domain=geoguessr.com
// @grant        GM_xmlhttpRequest
// @namespace http://tampermonkey.net/
// ==/UserScript==
(function() {
    'use strict';
    var u = 'https://www.geoguessr.com/api/v4/feed/friends?count=26';
    var map,heatmapLayer,streetViewContainer,streetViewMap
    let storedCoords
    let myNick
    let activities =JSON.parse(localStorage.getItem('activities'));
    let isUpdated=JSON.parse(localStorage.getItem('isUpdated'));
    getMyNick().then(nick => {
        myNick = nick;
    })
    .catch(error => {
        console.error("Failed to fetch user nick:", error);
    });
    if (!activities){
        activities={'duels':{},'games':{}};
    }

    function getMyNick() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://geoguessr.com/api/v3/profiles",
                onload: function(response) {
                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        const myId=data.user.id
                        const nickMap={}
                        nickMap[myId]=data.user.nick
                        resolve(nickMap);
                    } else {
                        console.error("Error fetching user data: " + response.statusText);
                        reject(null);
                    }
                },
                onerror: function(error) {
                    console.error("Error fetching user data:", error);
                    reject(null);
                }
            });
        });
    }

    function matchPlayerNick(gameData, friendMap) {
        gameData.forEach(game => {
            const playerId = game.playerId;
            Object.assign(friendMap,myNick)
            game.playerNick = friendMap[playerId] || "Opponents";
        });
        return gameData;
    }

    function getFriends() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://www.geoguessr.com/api/v3/social/friends/summary?page=0&fast=true",
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            const data = JSON.parse(response.responseText);
                            const friendMap = {};
                            data.friends.forEach(friend => {
                                friendMap[friend.userId] = friend.nick;
                            });
                            resolve(friendMap);
                        } catch (error) {
                            console.error("Error parsing JSON: ", error);
                            reject(null);
                        }
                    } else {
                        console.error("Error fetching user data: " + response.statusText);
                        reject(null);
                    }
                },
                onerror: function(error) {
                    console.error("Error fetching user data:", error);
                    reject(null);
                }
            });
        });
    }

    async function getFlagSvg(countryCode) {
        const url = `https://flagicons.lipis.dev/flags/4x3/${countryCode.toLowerCase()}.svg`;

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url,
                responseType: 'text',
                onload: (response) => {
                    if (response.status === 200) {
                        resolve(response.responseText);
                    } else {
                        reject(`Error fetching flag for ${countryCode}: ${response.statusText}`);
                    }
                },
                onerror: (error) => {
                    reject(`Error fetching flag for ${countryCode}: ${error}`);
                },
            });
        });
    }


    function getDiffdays(dataList) {
        const currentDate = new Date();
        return dataList.map(item => {
            if ('gameDate' in item) {
                const gameDate = new Date(item.gameDate);
                const timeDiff = Math.abs(currentDate - gameDate);
                const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
                item.gameDate = daysDiff;
            }
            return item;
        });
    }

    function saveLocalStorage(item) {
        localStorage.setItem('activities', JSON.stringify(item));
        activities =JSON.parse(localStorage.getItem('activities'));
        localStorage.setItem('isUpdated',JSON.stringify('updated'));

    }

    function downloadJSON(coords) {
        var data = JSON.stringify(coords, null, 2);
        var blob = new Blob([data], { type: 'application/json' });
        var url = URL.createObjectURL(blob);

        var a = document.createElement('a');
        a.href = url;
        a.download = 'coordinates.json';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    function clearLocalStorage() {
        localStorage.removeItem('activities');
        localStorage.removeItem('isUpdated');
    }

    function loadScript(url) {
        return new Promise((resolve, reject) => {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = url;
            script.async = true;
            script.onload = resolve;
            script.onerror = reject;
            document.body.appendChild(script);
        });
    }

    function loadGoogleMapsAPI(apiKey, libraries) {
        var librariesParam = libraries ? '&libraries=' + libraries.join(',') : '';
        var url = 'https://maps.googleapis.com/maps/api/js?key=' + apiKey + librariesParam;

        return new Promise((resolve, reject) => {
            if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
                loadScript(url)
                .then(resolve)
                .catch(() => {
                    reject(new Error('Failed to load Google Maps JavaScript API'));
                });
            } else {
                resolve();
            }
        });
    }

    function createHeatmap(coordinates) {
        let weightKey = 'score';
        let heatmapData=[]
        let filteredData
        let marker
        let markers = [];
        function filterHeatmapData(minScore, maxScore) {
            filteredData = heatmapData.filter(point => point.weight >= minScore && point.weight <= maxScore);
            heatmapLayer.setData(filteredData);
        }

        function createMap() {
                    var css = `
        #map-container {
            position: fixed;
            bottom: 0px;
            left: 40%;
            transform: translateX(-50%);
            width: 800px;
            height: 600px;
            z-index: 1;
        }

.control-panel {
    width: 120px;
    height: 30px;
    background-color: #fff;
    cursor: pointer;
    text-align: center;
    line-height: 30px;
}

.control-panel .select-container {
    display: none;
}

.control-panel:hover .select-container {
    display: block;
}

    .control-panel select {
        width: 100%;
        padding: 5px;
        border: 1px solid #ccc;
        border-radius: 5px;
        background-color: #fff;
        cursor: pointer;
        outline: none;
    }
    `;

            var style = document.createElement('style');
            style.textContent = css;
            document.head.appendChild(style);

            const mapContainer = document.createElement('div');
            mapContainer.id = 'map-container';
            document.body.appendChild(mapContainer);
            map = new google.maps.Map(mapContainer, {
                zoom: 2,
                center: { lat: 35.77, lng: 139.76 },
                mapTypeId: 'roadmap',
                gestureHandling: 'greedy',
                disableDefaultUI: true,
                streetViewControl: true
            });
            const controlDiv = document.createElement('div');
            controlDiv.className = 'control-panel';
            map.controls[google.maps.ControlPosition.TOP_LEFT].push(controlDiv);

            controlDiv.classList.add('hoverable');
            controlDiv.addEventListener('mouseenter', () => {
                selectContainer.style.display = 'block';
                controlDiv.style.height = '380px';
            });


            controlDiv.addEventListener('mouseleave', () => {
                selectContainer.style.display = 'none';
                controlDiv.style.height = '30px';
            });
            const layerCheckbox = document.createElement('input');
            layerCheckbox.type = 'checkbox';
            layerCheckbox.id = 'scatter'
            const label = document.createElement('label');
            label.htmlFor = 'scatter';
            label.textContent = 'Draw Scatter';
            label.style.color='#000'
            label.style.fontSize='16px'
            controlDiv.appendChild(layerCheckbox)
            controlDiv.appendChild(label);


const keysToFilter = ['playerNick', 'gameMode', 'forbidOptions', 'usingMap', 'country', 'score', 'distance', 'guessSeconds', 'gameDate'];
const optionValues = {};
let filters = [];
let players=[];

const selectContainer = document.createElement('div');
selectContainer.className = 'select-container';
controlDiv.appendChild(selectContainer);

keysToFilter.forEach(key => {

    const select = document.createElement('select');
    select.setAttribute('name', key);
    select.style.marginBottom = '15px';

    const defaultOption = document.createElement('option');
    defaultOption.setAttribute('value', '');
    defaultOption.textContent = `${key}`;
    select.appendChild(defaultOption);

    if (key === 'score' || key === 'distance' || key === 'guessSeconds') {
        const ranges = key === 'score' ? [
            { min: 0, max: 1499 },
            { min: 1500, max: 2999 },
            { min: 3000, max: 3999 },
            { min: 4000, max: 4499 },
            { min: 4500, max: 4999 },
            { min: 5000 }
        ] : key === 'distance' ? [
            { max: 0.5 },
            { min: 0.5, max: 5 },
            { min: 5, max: 100 },
            { min: 100, max: 1000 },
            { min: 1000, max: 5000 },
            { min: 5000 }
        ] : [
            { max: 5 },
            { min: 5, max: 15 },
            { min: 15, max: 30 },
            { min: 30, max: 60 },
            { min: 60, max: 300 },
            { min: 300 }
        ];

        for (let range of ranges) {
            const option = document.createElement('option');
            let label = range.max === undefined ? (key === 'score' ? '5000' : key === 'distance' ? '>5000km' : '>300s') : (key === 'score' ? `${range.min}-${range.max}` : key === 'distance' ? `${range.min}-${range.max}km` : `${range.min}-${range.max}s`);
            if (range.min==undefined&&key==='distance'){
                label='<0.5km';
            }
            if (range.min==undefined&&key==='guessSeconds'){
                label='<5s';
            }
            option.setAttribute('value', label);
            option.textContent = label;
            select.appendChild(option);
        }
    }
    else if (key === 'gameDate') {
        const dateRanges = [
            { label: 'More than 1 month', min:30, max: 1000 },
            { label: 'More than 15 days', min: 15, max: 30 },
            { label: 'More than 1 week', min: 7, max: 15 },
            { label: 'More than 1 day', min: 1, max: 7 },
            { label: 'Within 24 hours', min: 0, max: 1 }
        ];

        dateRanges.forEach(dateRange => {
            const option = document.createElement('option');
            option.setAttribute('value', `${dateRange.min}-${dateRange.max}` );
            option.textContent = dateRange.label;
            select.appendChild(option);

        });
    }
    else {
        const optionCounts = {};
        coordinates.forEach(item => {
            const value = item[key];
            optionCounts[value] = (optionCounts[value] || 0) + 1;
        });
        const sortedOptions = Object.keys(optionCounts).sort((a, b) => optionCounts[b] - optionCounts[a]);
        sortedOptions.forEach(value => {
            if (!optionValues[value]) {
                optionValues[value] = true;
                const option = document.createElement('option');
                option.setAttribute('value', value);
                if (key === 'playerNick') {
                    const myKey = Object.keys(myNick);
                    option.textContent = value;
                    if (value == myNick[myKey]) {
                        option.textContent = `${value}(me)`;
                        option.style.color = 'red';
                    }
                } else {
                    option.textContent = value;
                }
                select.appendChild(option);
            }
        });
    }

    selectContainer.appendChild(select);


select.addEventListener('change', () => {
    const selectedValue = select.value;
    filters[key] = selectedValue;

    let filteredData = coordinates

    Object.keys(filters).forEach(filterKey => {
        const filterValue = filters[filterKey];
        if (filterValue) {
            if (filterValue.includes('-')) {

                filteredData = filteredData.filter(item => {
                    if (filterKey=='gameDate'){
                        const [minDays, maxDays] = filterValue.split('-')
                        const itemValue = parseFloat(item[filterKey])
                        return itemValue > minDays && itemValue <= maxDays;
                    }
                    else{
                        const [minValue, maxValue] = filterValue.split('-').map(val => parseFloat(val));
                        const itemValue = parseFloat(item[filterKey]);
                        return itemValue >= minValue && itemValue <= maxValue;}
                });
            } else if (filterValue.includes('>') || filterValue.includes('<')) {
                const operator = filterValue.includes('>') ? '>' : '<';
                const value = parseFloat(filterValue.substring(1));
                filteredData = filteredData.filter(item => {
                    const itemValue = parseFloat(item[filterKey]);
                    return operator === '>' ? itemValue > value : itemValue < value;
                });
            } else if (filterValue.includes('5000')) {
                filteredData = filteredData.filter(item => {
                    const itemValue = parseFloat(item[filterKey]);
                    return itemValue === 5000;
                });}
             else {
                filteredData = filteredData.filter(item => item[filterKey] === filterValue);
            }
        }
    });

    refreshHeatmap(filteredData, 'score');
});
            });

            layerCheckbox.addEventListener('change', (event) => {
                if (heatmapData.length==0){
                    for (const coord of coordinates) {
                        heatmapData.push({
                            location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                            weight: coord.score
                        });
        }
                }
                if (event.target.checked) {
                    for (const coord of heatmapData) {
                        const marker = new google.maps.Marker({
                            position: new google.maps.LatLng(coord.location),
                            map: map,
                            title: 'Score: ' + coord.weight,
                            pin: {
                                fillOpacity: 1,
                                strokeWeight: 0,
                                color:'gray',
                                scale: (coord.weight/5000)
                            }
                        });
                        markers.push(marker);
                        marker.addListener('click', () => {
                            createStreetViewContainer(coord.location.lat(),coord.location.lng())

                        });
                    }



                } else {
                        markers.forEach(marker => marker.setMap(null));
                        markers = [];

                       }
            })

        }

        function refreshHeatmap(fd,k) {
            if (fd.length === 0) {
                console.error('No valid coordinates data found');
            }
            heatmapData=[]
            for (const coord of fd) {
                heatmapData.push({
                    location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                    weight: coord[k]
                });
            }
            heatmapLayer.setData(heatmapData);
            if (markers.length!=0){
                markers.forEach(marker => marker.setMap(null));
                markers = [];
            }
        }

        if (!map) {
            createMap();
            getDefault(coordinates)
        }

    }

    function getDefault(coordinates){
        var h=[];
        if (heatmapLayer) {
            heatmapLayer.setMap(null);
        }
        for (const coord of coordinates) {
            h.push({
                location: new google.maps.LatLng(coord.latLng.lat, coord.latLng.lng),
                weight: coord.score
            });
        }

        heatmapLayer = new google.maps.visualization.HeatmapLayer({
            data: h,
            dissipating: true,
            map: map,
        });}

    function getStatics(url, result, maxPages,currentPage = 1, pageToken = null) {
        if (currentPage==1){
            const swal = Swal.fire({
                title: 'Fetching Activities',
                text: 'Please wait...',
                allowOutsideClick: false,
                allowEscapeKey: false,
                showConfirmButton: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
        }
        if (currentPage > maxPages) {
            console.log(`Reached maximum number of pages (${maxPages}). Stopping requests.`);
            swal.close()
            Swal.fire('Success', 'All activities retrieved successfully!', 'success');
            saveLocalStorage(result)
            getCoords(result);
            return;
        }

        let nextPageUrl = url;
        if (pageToken) {
            nextPageUrl += `&paginationToken=${encodeURIComponent(pageToken)}`;
        }

        GM_xmlhttpRequest({
            method: "GET",
            url: nextPageUrl,
            onload: function(response) {
                if (response.status === 200) {
                    const data = JSON.parse(response.responseText);
                    processActivities(data, result);
                    const nextPageToken = data.paginationToken;
                    if (nextPageToken) {
                        getStatics(url, result, maxPages, currentPage + 1, nextPageToken);
                    } else {
                        Swal.fire('Success', 'All activities retrieved successfully!', 'success');
                        saveLocalStorage(result)
                        getCoords(result);
                    }
                } else {
                    console.error('Request failed: ' + response.statusText);
                    Swal.fire('Error', 'Failed to fetch activities. Please try again later.', 'error');
                }
            },
            onerror: function(response) {
                console.error('Request failed: ' + response.statusText);
                Swal.fire('Error', 'Failed to fetch activities. Please try again later.', 'error');
            }
        });
    }

    function processActivities(data, result) {
        const entries = data.entries;
        if (entries && entries.length > 0) {
            entries.forEach(entry => {
                if (entry.payload) {
                    const payloadList = JSON.parse(entry.payload);
                    const userId = entry.user.id;
                    if (!Array.isArray(payloadList)) {
                        processPayload(payloadList, userId, result);
                    } else {
                        payloadList.forEach(payload => {
                            processPayload(payload, userId, result);
                        });
                    }
                }
            });
        } else {
            console.error('Data not found!');
        }
    }

    function processPayload(payload, userId, result) {
        if (payload.gameToken) {
            result.games[userId] = result.games[userId] || [];
            if (!result.games[userId].includes(payload.gameToken)) {
                result.games[userId].push(payload.gameToken);
            }
        } else if (payload.gameId) {
            result.duels[userId] = result.duels[userId] || [];
            if (!result.duels[userId].includes(payload.gameId)) {
                result.duels[userId].push(payload.gameId);
            }
        }
    }

    async function getCoords(data) {
        try {
            const coordinates = [];
            const duelsPromises = [];
            const gamesPromises = [];
            const chunkSize = 20;
            const swal = Swal.fire({
                title: 'Fetching Coordinates',
                text: 'Please wait...',
                allowOutsideClick: false,
                allowEscapeKey: false,
                showConfirmButton: false,
                didOpen: () => {
                    Swal.showLoading();
                }
            });
            for (const gameIds of Object.values(data.duels)) {
                for (let i = 0; i < gameIds.length; i += chunkSize) {
                    const chunk = gameIds.slice(i, i + chunkSize);
                    const chunkPromises = chunk.map(gameId => {
                        const requestUrl = `https://game-server.geoguessr.com/api/duels/${gameId}`;
                        return getGameSummary(requestUrl, 'duels', coordinates);
                    });
                    duelsPromises.push(Promise.all(chunkPromises));
                }
            }

            for (const gameIds of Object.values(data.games)) {
                for (let i = 0; i < gameIds.length; i += chunkSize) {
                    const chunk = gameIds.slice(i, i + chunkSize);
                    const chunkPromises = chunk.map(gameId => {
                        const requestUrl = `https://www.geoguessr.com/api/v3/games/${gameId}?client=web`;
                        return getGameSummary(requestUrl, 'games', coordinates);
                    });
                    gamesPromises.push(Promise.all(chunkPromises));
                }
            }

            await Promise.all([...duelsPromises, ...gamesPromises]);
            swal.close();
            try {
                const friends = await getFriends();
                const matchedData = matchPlayerNick(coordinates, friends);
                coordinates=matchedData
                createHeatmap(matchedData);
                Swal.fire('Success', 'Heatmap is prepared!', 'success');
            } catch (error) {
                console.error("Error:", error);
                Swal.fire('Error', 'Failed to prepare heatmap', 'error');
            }
            await downloadJSON(coordinates);
        } catch (error) {
            console.error('Error parsing JSON from localStorage:', error);
        }
    }

    async function getGameSummary(url, mode, coordinates) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function(response) {
                    try {let forbidOptions
                        const data = JSON.parse(response.responseText);
                        if(data.teams||data.player){
                            if (mode === 'duels') {
                                const movementOptions=data.movementOptions
                                if(movementOptions.forbidMoving&&movementOptions.forbidZooming&&movementOptions.forbidRotating){forbidOptions='NMPZ'}
                                else if (!movementOptions.forbidMoving&&!movementOptions.forbidZooming&&!movementOptions.forbidRotating){forbidOptions='Moving'}
                                else if(movementOptions.forbidMoving&&!movementOptions.forbidZooming&&!movementOptions.forbidRotating){forbidOptions='NoMoving'}
                                else{forbidOptions='Entertainment'}
                            const usingMap=data.options.map.name
                            data.teams.forEach(team => {
                                team.players.forEach(player => {
                                    player.guesses.forEach(guess => {
                                        const roundNumber = guess.roundNumber;
                                        const roundData = data.rounds.find(round => round.roundNumber === roundNumber);
                                        if (roundData) {
                                            const gameDate = guess.created.substring(0, 10);
                                            const gameMode = 'duels';
                                            const latLng = { 'lat': roundData.panorama.lat, 'lng': roundData.panorama.lng };
                                            const country = roundData.panorama.countryCode;
                                            const playerId = player.playerId;
                                            const score = team.roundResults.find(result => result.roundNumber === roundNumber).score;
                                            const distance = (guess.distance / 1000).toFixed(2);
                                            const guessSeconds = (Math.abs(new Date(guess.created) - new Date(roundData.startTime)) / 1000).toFixed(2);
                                            coordinates.push({ gameMode, playerId, latLng, country, score, distance, guessSeconds, forbidOptions, usingMap, gameDate });
                                        }
                                    });
                                });
                            });
                        }
                        else {
                            if(data.forbidMoving&&data.forbidZooming&&data.forbidRotating){forbidOptions='NMPZ'}
                            else if (!data.forbidMoving&&!data.forbidZooming&&!data.forbidRotating){forbidOptions='Moving'}
                            else if(data.forbidMoving&&!data.forbidZooming&&!data.forbidRotating){forbidOptions='NoMoving'}
                            else{forbidOptions='Entertainment'}
                            const gameMode = 'classic';
                            const player = data.player;
                            const playerId = player.id;
                            const usingMap=data.mapName
                            player.guesses.forEach((guess, index) => {
                                const roundData = data.rounds[index];
                                const gameDate = roundData.startTime.substring(0, 10);
                                const latLng = { 'lat': roundData.lat, 'lng': roundData.lng };
                                const country = roundData.streakLocationCode;
                                const score = guess.roundScoreInPoints;
                                const distance = parseFloat(guess.distance.meters.amount);
                                const guessSeconds = guess.time;
                                coordinates.push({ gameMode, playerId, latLng, country, score, distance, guessSeconds, forbidOptions, usingMap, gameDate });
                            });
                        }}
                        resolve();
                    } catch (error) {
                        console.error(`Error parsing JSON from URL: ${url}`, error);
                    }
                },
                onerror: function(error) {
                    reject(error);
                }
            });
        });
    }

    async function getPlayerName(id) {
        return new Promise((resolve, reject) => {
            const url = `https://www.geoguessr.com/user/${id}`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                onload: function(response) {
                    if (response.status === 200) {
                        const playerName = extractPlayerName(response.responseText);
                        resolve(playerName);
                    } else {
                        reject('Error:', response.status);
                    }
                },
                onerror: function(error) {
                    reject('Error:', error);
                }
            });
        });
    }

    function extractPlayerName(responseText) {
        const regex = /"user"\s*:\s*{\s*"nick"\s*:\s*"(.+?)"/;
        const match = responseText.match(regex);
        if (match && match.length > 1) {
            return match[1];
        }
        return null;
    }

    function createStreetViewContainer(latitude, longitude,mapContainer) {
        if (streetViewContainer) {
            streetViewContainer.remove();
        }
        if (streetViewMap){streetViewMap.setStreetView(null)
        }
        streetViewContainer = document.createElement('div');
        streetViewContainer.id = 'street-view-container';
        streetViewContainer.style.position = 'fixed';
        streetViewContainer.style.bottom = '0px';
        streetViewContainer.style.left = '0px';
        streetViewContainer.style.width = '800px';
        streetViewContainer.style.height = '600px';
        streetViewContainer.style.overflow = 'hidden';
        streetViewContainer.style.zIndex = '9999';

        streetViewMap = new google.maps.Map(streetViewContainer, {
            center: { lat: latitude, lng: longitude },
            zoom: 14,
            streetViewControl: true
        });


        var streetViewService = new google.maps.StreetViewService();
        streetViewService.getPanorama({ location: { lat: latitude, lng: longitude }, radius: 50 }, function(data, status) {
            if (status === 'OK') {
                var streetView = new google.maps.StreetViewPanorama(streetViewContainer, {
                    position: data.location.latLng,
                    pov: { heading: 235, pitch: 10 }
                });
                streetViewMap.setStreetView(streetView);
            } else {
                console.error('Street View data not found for this location');
            }
        });

        document.body.appendChild(streetViewContainer);
    }

    async function swalOption(){
        const { value: inputOption,dismiss: inputDismiss } =await Swal.fire({
            title: 'Get Activities',
            text: 'Do you want to fetch activities from your Geoguessr account? If you click "Cancel", you will need to upload a JSON file',
            icon: 'question',
            showCancelButton: true,
            showCloseButton:true,
            allowOutsideClick: false,
            input: 'number',
            inputLabel: 'Set A Limit Of Activities Pages',
            inputPlaceholder: '10',
            inputAttributes: {
                min:1,
                max: 100,
                step:10,
            },
            confirmButtonColor: '#3085d6',
            cancelButtonColor: '#d33',
            confirmButtonText: 'Yes',
            cancelButtonText: 'Cancel',
            inputValidator: (value) => {
                if (!value||parseInt(value)<1) {
                    return 'Please set a valid limit number';
                }
                if (parseInt(value) > 100) {
                    return 'It is recommended that the maximum number of pages should not exceed 100!';
                }
            }
        });

        if (inputOption) {
            if (!isUpdated){
                const pageValue=parseInt(inputOption)
                getStatics(u,activities,pageValue)}
            else{
                getCoords(activities)}
        }
        else if(inputDismiss==='cancel'){
            const input = document.createElement('input');
            input.type = 'file';
            input.style.display = 'none'
            document.body.appendChild(input);

            const coordsPromise = new Promise((resolve) => {
                input.addEventListener('change', async () => {
                    const file = input.files[0];
                    const reader = new FileReader();

                    reader.onload = (event) => {
                        try {
                            const result = JSON.parse(event.target.result);
                            resolve(result);

                            document.body.removeChild(input);
                        } catch (error) {
                            Swal.fire('Error Parsing JSON Data!', 'The input JSON data is invalid or incorrectly formatted.','error');
                        }
                    };

                    reader.readAsText(file);
                });


                input.click();
            });
            coordsPromise.then(async (data) => {
                try {
                    const friends = await getFriends();
                    var matchedData = matchPlayerNick(data, friends);
                    matchedData=getDiffdays(matchedData)
                    createHeatmap(matchedData);
                    Swal.fire('Success', 'Heatmap is prepared!', 'success');
                } catch (error) {
                    console.error("Error:", error);
                    Swal.fire('Error', 'Failed to prepare heatmap', 'error');
                }
});
        }
    }

    function createButton() {

        var mapButton = document.createElement('button');
        mapButton.textContent = 'Create Heatmap';
        mapButton.addEventListener('click',swalOption);
        mapButton.style.position = 'fixed';
        mapButton.style.top = '10px';
        mapButton.style.right = '360px';
        mapButton.style.zIndex = '9999';
        document.body.appendChild(mapButton);

        var refreshButton = document.createElement('button');
        refreshButton.textContent = 'Update Your Activities';
        refreshButton.addEventListener('click', function(){getStatics(u,activities,10)});
        refreshButton.style.position = 'fixed';
        refreshButton.style.top = '10px';
        refreshButton.style.right = '200px';
        refreshButton.style.zIndex = '9999';
        document.body.appendChild(refreshButton);

        var clearButton = document.createElement('button');
        clearButton.textContent = 'Clear Activities';
        clearButton.addEventListener('click', clearLocalStorage);
        clearButton.style.position = 'fixed';
        clearButton.style.top = '10px';
        clearButton.style.right = '80px';
        clearButton.style.zIndex = '9999';
        document.body.appendChild(clearButton);
    }

    loadGoogleMapsAPI('AIzaSyAiRLvmrxcqZRhsiPMzK5Ps2b5Ov6XhJrY', ['visualization','streetView'])
    .then(createButton)
    .catch(error => {
        console.error(error);
    });
})();