Better Scoreboard [ Geoguessr ] [ geoguessr.com ]

Improved lookup tool for geoguesser challenge leaderboard.

目前为 2022-10-22 提交的版本。查看 最新版本

// ==UserScript==
// @name       Better Scoreboard [ Geoguessr ] [ geoguessr.com ] 
// @version    1.3.6
// @author     Han75 - @Han75#4985
// @description  Improved lookup tool for geoguesser challenge leaderboard.
// @match      https://www.geoguessr.com/*
// @require http://code.jquery.com/jquery-latest.js
// @namespace han75.com
// ==/UserScript==




/*
* API endpoint for the Geoguessr challenge mode
*
* Edpoint: api/ v3/ results/ scores/ <match ID>/ <Lowest_Checked(max:50)>
*/
const endpoint = "https://www.geoguessr.com/api/v3/results/scores/";
/**
* Data is holds every record collected from geoguessr's API.
* DATA IS
* Map<String : Map<String..(4),Number..(2),Array< Number >..(2) >>
* WHERE
* {ID:DATA}= {Scoreboard_Pos : Name, uid, pfp, Game_Token, Total_Tcore, Total_Distance, All_Scores, All_Distances, {All API "guesses" data}}}
*/
var data = {}
/*
* nameMap IS
* Map< String :  STRING >
*  WHERE
*  {NAME : ID} = {name:coreBd_Pos}
*/
var nameMap = {}
// Number of players in the lobby
let numPlayers = 0;
$(document).ready(function () {

    //render context    
    waitForKeyElements(".results_switch__Qj1HI", start);

});
function start() {
    id = $(`meta[property='og:url']`).attr("content").split("/")[4];
    $('.results_switch__Qj1HI').after('<div id="bsbHeaderContainer"></div>');
    $('#bsbHeaderContainer').after('<div id="bsbBodyContainer"></div>');
    console.log('should now set css');

    $('#bsbHeaderContainer').append('<div id="bsbHeader"></div>');
    /** HEADER */
    $('#bsbHeader').append(`<div id="bsbInfo"></div>`);
    $('#bsbHeader').append('<span class="bsbSearchTab" id="singularTab">Search Single Record</span>');
    $('#bsbHeader').append(`<div class="bsbSearchContainer" id="singularContainer"></div>`);
    $('#bsbHeader').append('<span class="bsbSearchTab" id="rangeTab">Search Record Range(max:50)</span>');
    $('#bsbHeader').append(`<div class="bsbSearchContainer" id="rangeContainer"></div>`);


    $('#bsbInfo').append('<h2 class=`bsbInfoText`>Better Scoreboard</h2>');
    $('#bsbInfo').append('<h4 class=`bsbInfoText`>By Han75</h4>');

    

    $('#singularContainer').append(`<div class="bsbSearchOption" name='rankSearch'></div>`);
    $('#singularContainer').append(`<div class="bsbSearchOption" name='nameSearch'></div>`);

    $('.bsbSearchOption[name="rankSearch"]').append(`<label class="bsbInputLbl" for="searchPosition">Rank:</label><input type="number" min="1" id="searchPosition"><button class="bsbGuiBtn" id="bsbSearchPosBtn">Search By Rank</button>`);
    $('.bsbSearchOption[name="nameSearch"]').append(`<label class="bsbInputLbl for="searchName">Username:</label><input type="text" min="1" id="searchName"><button class="bsbGuiBtn" id="bsbSearchNameBtn">Search By Username</button>`);

   
    $('#rangeContainer').append(`<div class="bsbSearchOption" name='rangeFirst'></div>`);
    $('#rangeContainer').append(`<div class="bsbSearchOption" name='rangeLast'></div>`);
    $('.bsbSearchOption[name="rangeFirst"]').append(`<label class="bsbInputLbl for="searchRangeFirst">First:</label><input type="Number" id="searchRangeFirst">`);
    $('.bsbSearchOption[name="rangeLast"]').append(`<label class="bsbInputLbl for="searchRangeLast">Last:</label><input type="Number" id="searchRangeLast"><button class="bsbGuiBtn" id="bsbSearchRangeBtn">Search Range</button>`);
    $('#bsbHeader').append('<a id="exportRecords">Export All Records As .json</a>')
    
    /**BODY */
    $('#bsbBodyContainer').append('<div class="results_table__FHKQm" id="bsbBody"></div>');
    $('#bsbBodyContainer').after('<span id="closeDefaultScoreboard">Click to Toggle Default Scoreboard</span>')

    /* ALL HTML GOES ABOVE THIS LINE*/
    /* ALL CSS GOES BELOW THIS LINE*/
    $('#bsbHeader').css({ "display": "flex", 'flex-direction': 'column',"background-color": "#4D5180", "text-align": "center","border-radius":"25px" });
    
    $('#bsbHeaderContainer').css({"width":"60%", "display": "flex", 'flex-direction': 'column' });
    $('.bsbSearchContainer').css({ "display": "flex", "flex-direction": "row" });
    $('.bsbSearchTab').css({ "display": "block", "text-align": "left", "background-color": "rgb(121,80,229)", "padding": "5px" });
    
    $('.bsbSearchTab').mouseover(function () {
        $(this).css({ "background-color": "rgb(157,41,56)", "cursor": "pointer" });
    }).mouseout(function () {
        $(this).css({ "background-color": "rgb(121,80,229)", "cursor": "auto" });
    });
    /***Search Singular Tab Animate Open and Close */
    $('#singularTab').click(function () {
        $('#singularContainer').slideToggle({
            "opacity": "show",
            "bottom": "100"
        }, 500);
        //$('#singularTab').css("background-color","#4D5180");
        $('#singularTab').addClass('singularTriggerClose');
    });
    
    $('.singularTriggerClose').click(function () {
        $('#singularContainer').slideToggle({ "opacity": "show", "top": "100" }, 500);
        //$('#singularTab').css("background-color","rgb(121,80,229)");
        $('#singularTab').removeClass('singularTriggerClose');
    });
    /***Search Range Tab Animate Open and Close */
    $('#rangeTab').click(function () {
        $('#rangeContainer').slideToggle({
            "opacity": "show",
            "bottom": "100"
        }, 500);
        //$('#rangeTab').css("background-color","#4D5180");
        $('#rangeTab').addClass('rangeTriggerClose');
    });
    $('.rangeTriggerClose').click(function () {
        $('#rangeContainer').slideToggle({ "opacity": "show", "top": "100" }, 500);
        //$('#rangeTab').css("background-color","rgb(121,80,229)");
        $('#rangeTab').removeClass('rangeTriggerClose');
    });
    $('#rangeTab').click();
    $('.bsbSearchOption').css({"width":"50%", "border": "1px solid rgb(70,35,57)", "border-radius": "5px", "display": "flex", "flex-direction": "column" });
    $(".bsgGuiBtn").css({"padding":"10px"})
    $("#bsbSearchPosBtn").prop("disabled", true);
    $("#bsbSearchNameBtn").prop("disabled", true);
    $("#bsbSearchRangeBtn").prop("disabled", true);
    $("#bsbSearchPosBtn").click(findPosition);
    $("#bsbSearchNameBtn").click(findUsername);
    $("#bsbSearchRangeBtn").click(findRange);
    $("#exportRecords").click(download);
    $("#exportRecords").css({
        "color": "white",
        "padding": "10px",
        "font-weight": "bold"
    });
    $(".bsb")
    $('.bsbGuiBtn').css({"border":"none","padding":"10px","font-family":"var(--font-neo-sans);","background-color": "rgb(71,62,96)","color":"white"})
    $('.bsbGuiBtn').mouseover(function () {
        $(this).css({ "background-color": "rgb(26,26,46)", "cursor": "pointer" });
    }).mouseout(function () {
        $(this).css({ "background-color": "rgb(71,62,96)", "cursor": "auto" });
    });
    $('#exportRecords').mouseover(function () {
        $(this).css({"cursor": "pointer" });
    }).mouseout(function () {
        $(this).css({"cursor": "auto" });
    });
    $('#bsbBodyContainer').css("width","100%");
    $('.bsbInputLbl').css({"padding":"4px","font-weight":"bold"});
    $('#bsbInfo').css("padding","10px");
    
    $('#closeDefaultScoreboard').css({ "font-weight":"bold","display": "block", "text-align": "left", "background-color": "rgb(121,80,229)", "padding": "10px","width":"100%" });
    
    $('#closeDefaultScoreboard').click(function () {
        $('.results_container__9fcR8').find("> .results_table__FHKQm").slideToggle({
            "opacity": "show",
            "bottom": "100"
        }, 500);
        //$('#singularTab').css("background-color","#4D5180");
    });
    $('.cldTriggerClose').click(function () {
        $('.results_container__9fcR8').find("> .results_table__FHKQm").slideToggle({ "opacity": "show", "top": "100" }, 500);
        //$('#singularTab').css("background-color","rgb(121,80,229)");
        $('#closeDefaultScoreboard').removeClass('cldTriggerClose');
    });
    $('#closeDefaultScoreboard').mouseover(function () {
        $(this).css({ "background-color": "rgb(157,41,56)", "cursor": "pointer" });
    }).mouseout(function () {
        $(this).css({ "background-color": "rgb(121,80,229)", "cursor": "auto" });
    });
    // The final stack trace is || RoundUp(NumPlayers/50) api requests  <- getData() <- getNumPlayers(log(playercount) api requests)
    // I thought this was a bad choice at first, but it’s probably better than accidentally sending sh*t loads of API requests because of preliminary fetch delays and getting slammed with a rate limit 
    //This ones for testing, only 100 records
    //findNumberOfPlayers(0,100);
    //This ones for final
    
    findNumberOfPlayers(0,6000);
}

/**
 * Finds how many players are in a lobby
 * @param {2 Numbers} LowerBound,UpperBound
 */
async function findNumberOfPlayers(lowerBound, upperBound) {

    //Binary search babyy 
    if (Math.ceil(lowerBound) >= Math.floor(upperBound) - 1) {
        getData(Math.ceil(lowerBound));
    } else {
        let midpoint = Math.ceil((lowerBound + upperBound) / 2);
        //Fetch 1 entry from API, change search bounds and search again
        fetch(`${endpoint}${id}/${midpoint}/1`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json())
            .then((resp) => {
                if (resp.length == 0) {
                    findNumberOfPlayers(lowerBound, midpoint);
                } else {
                    findNumberOfPlayers(midpoint, upperBound);
                }

            });;
    }


}
/**
 * Loads all of the data and stores it in data dictionary.
 * 
 */
async function getData(nPlayers) {
    numPlayers = nPlayers;


    //Fetch all the players.
    //Can fetch a max of 50 datapoints at a time 
    for (let i = 0; i < nPlayers; i += 50) {
        //accept parameter is used by native application and I added it to ensure that it Satan himself(CORS) doesn't stop me
        fetch(`${endpoint}${id}/${i}/50`, { accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" }).then((response) => response.json()).then((resp) => {
            // I hate the variable name. resp ? Like ew whyd i do that 
            for (let k = 0; k < resp.length; k++) {
                let scores = [];
                let dists = [];
                let tDist = 0;
                for (let round = 0; round < resp[k]["game"]["player"]["guesses"].length; round++) {

                    scores.push(resp[k]["game"]["player"]["guesses"][round]["roundScoreInPoints"]);
                    dists.push((resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000).toFixed(3));
                    tDist += resp[k]["game"]["player"]["guesses"][round]["distanceInMeters"] / 1000;
                }
                data[i + k] = { "name": resp[k]["playerName"], "uid": resp[k]["userId"], "pic": resp[k]["pinUrl"], "gametoken": resp[k]["gameToken"], "totscore": resp[k]["game"]["player"]["totalScore"]["amount"], "totDist": tDist.toFixed(3), "scores": scores, "dists": dists, "guesses":resp[k]["game"]["player"]["guesses"]}
                // Map user name to score for 1 : 1 correlation 
                nameMap[resp[k]["playerName"].toLowerCase()] = i + k;
            }

        })
    }
    
    $('#bsbInfo').append(`<p>${nPlayers + 1} players </p>`);
    $("#bsbSearchPosBtn").prop("disabled", false);
    $("#bsbSearchNameBtn").prop("disabled", false);
    $('#bsbSearchRangeBtn').prop("disabled",false);
}
// TODO: I want the @p to say “N records found (download)”
function download() {
    //TODO: do this function later take a break, jesus.
    console.log(`File will have ${Object.keys(data).length} entries`);
    var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data));
    console.log(`File will have ${Object.keys(data).length} entries`);
    $('#exportRecords').attr("href", dataStr);
    $('#exportRecords').attr("download", `Geoguessr_Export_n${numPlayers}.json`);
}
/**
 * Searches data by Scoreboard Position from the input box
 */
// refactor to a better name
function findPosition() {
    let position = Math.floor($('#searchPosition').val() - 1);
    if (position in data) {
        let a = data[position];
        // I’m not gonna cap I straight up ripped these fancy ahh stylized ahh divs straight from the results table, whose gonna stop me? 😈
        $("#bsbBody").empty();
        $("#bsbBody").append(`<div class="results_row__2iTV4 results_headerRow__C91Ks"><div></div><div class="results_hideOnSmallScreen__hrv5O">Round 1</div><div class="results_hideOnSmallScreen__hrv5O">Round 2</div><div class="results_hideOnSmallScreen__hrv5O">Round 3</div><div class="results_hideOnSmallScreen__hrv5O">Round 4</div><div class="results_hideOnSmallScreen__hrv5O">Round 5</div><div>Total</div></div>`);
        //Literally what is this naming convention? Nothing about this makes sense. You don’t have to make it this hard on yourself, @GeoGuessr’s singular front end engineer

        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${position + 1}.</span><div class="results_userLink__V6cBI"><a target="_blank" href="/user/${a["uid"]}"><div class="user-nick_root__DUfvc"><div class="user-nick_avatar__lW3e2"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG"><img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="Efesoturum" loading="auto" style="object-fit: cover;"></div></div></div></div></div></div><div class="user-nick_nickWrapper__8Tnk4"><div class="user-nick_nick__y4VIt">${a["name"]}&nbsp;</div></div></div></a></div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][0]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][1]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][2]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][3]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][4]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`);

        // When I click the div I open game overview in a new tab
        // I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated 
        $("#bsbEntry").click(function (e) {
            e.preventDefault();
            window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
        });
    } else if (position < 1 || position > numPlayers) {

        $('#searchPosition').val("");
    } else {
        $("#bsbBody").empty();
        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Undefined error or you broke the script probably. This isn’t gonna show up any other way </div>`);
    }
}
/**
 * searches data by username
 */
function findUsername() {
    // Get that shi from the input box. MAN I love jquery 
    let name = $('#searchName').val();
    if (name.toLowerCase() in nameMap) {
        let position = nameMap[name];
        let a = data[nameMap[name]];
        // Everything below this like is exactly the same as searchPosition

        $("#bsbBody").empty();
        $("#bsbBody").append(`<div class="results_row__2iTV4 results_headerRow__C91Ks"><div></div><div class="results_hideOnSmallScreen__hrv5O">Round 1</div><div class="results_hideOnSmallScreen__hrv5O">Round 2</div><div class="results_hideOnSmallScreen__hrv5O">Round 3</div><div class="results_hideOnSmallScreen__hrv5O">Round 4</div><div class="results_hideOnSmallScreen__hrv5O">Round 5</div><div>Total</div></div>`);
        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${position + 1}.</span><div class="results_userLink__V6cBI"><a target="_blank" href="/user/${a["uid"]}"><div class="user-nick_root__DUfvc"><div class="user-nick_avatar__lW3e2"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG"><img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="Efesoturum" loading="auto" style="object-fit: cover;"></div></div></div></div></div></div><div class="user-nick_nickWrapper__8Tnk4"><div class="user-nick_nick__y4VIt">${a["name"]}&nbsp;</div></div></div></a></div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][0]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][1]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][2]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][3]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
        </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][4]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`);

        $("#bsbEntry").click(function (e) {
            e.preventDefault();
            window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);

        });

    } else {
        $("#bsbBody").empty();
        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">User not found</div>`);
    }

}
function findRange(){
    let rStart = Math.floor($("#searchRangeFirst").val())-1;
    let rEnd = Math.floor($("#searchRangeLast").val())-1;
    let n = rEnd-rStart;
    $("#bsbBody").empty();
    if(rEnd>numPlayers||rEnd<0||rStart>numPlayers||rStart<0){
        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Invalid search bounds. Please change your search</div>`);
    }else if(n<=0||n>50){
        $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry">Number of records to search must be between 0 and 50</div>`);
    }else{
        $("#bsbBody").append(`<div class="results_row__2iTV4 results_headerRow__C91Ks"><div></div><div class="results_hideOnSmallScreen__hrv5O">Round 1</div><div class="results_hideOnSmallScreen__hrv5O">Round 2</div><div class="results_hideOnSmallScreen__hrv5O">Round 3</div><div class="results_hideOnSmallScreen__hrv5O">Round 4</div><div class="results_hideOnSmallScreen__hrv5O">Round 5</div><div>Total</div></div>`);   
        for(let i=rStart;i<rEnd;i++){
            let a = data[i];
           
            $("#bsbBody").append(`<div class="results_row__2iTV4" id="bsbEntry${i}"><div class="results_column__BTeok results_player__F8U_T"><span class="results_position__KWMOY">${i + 1}.</span><div class="results_userLink__V6cBI"><a target="_blank" href="/user/${a["uid"]}"><div class="user-nick_root__DUfvc"><div class="user-nick_avatar__lW3e2"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_circle__QFYEk styles_variantFloating__Srm_N styles_colorWhite__Y640w styles_borderSizeFactorOne__8iP_3"><div class="styles_rectangle___6gqv" style="padding-top: 100%;"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG"><img src="/images/auto/48/48/ce/0/plain/${a["pic"]}" class="styles_image__8M_kp" alt="Efesoturum" loading="auto" style="object-fit: cover;"></div></div></div></div></div></div><div class="user-nick_nickWrapper__8Tnk4"><div class="user-nick_nick__y4VIt">${a["name"]}&nbsp;</div></div></div></a></div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][0]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][0]["distance"]["miles"]["amount"]} ${a["guesses"][0]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][0]["time"]/60)==0?"":Math.floor(a["guesses"][0]["time"]/60)} ${Math.floor(a["guesses"][0]["time"]/60)==0?"":"min,"} ${a["guesses"][0]["time"]%60} sec
            </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][1]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][1]["distance"]["miles"]["amount"]} ${a["guesses"][1]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][1]["time"]/60)==0?"":Math.floor(a["guesses"][1]["time"]/60)} ${Math.floor(a["guesses"][1]["time"]/60)==0?"":"min,"} ${a["guesses"][1]["time"]%60} sec</div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][2]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][2]["distance"]["miles"]["amount"]} ${a["guesses"][2]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][2]["time"]/60)==0?"":Math.floor(a["guesses"][2]["time"]/60)} ${Math.floor(a["guesses"][2]["time"]/60)==0?"":"min,"} ${a["guesses"][2]["time"]%60} sec
            </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][3]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][3]["distance"]["miles"]["amount"]} ${a["guesses"][3]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][3]["time"]/60)==0?"":Math.floor(a["guesses"][3]["time"]/60)} ${Math.floor(a["guesses"][3]["time"]/60)==0?"":"min,"} ${a["guesses"][3]["time"]%60} sec
            </div></div><div class="results_column__BTeok results_hideOnSmallScreen__hrv5O"><div class="results_score__jUqyZ">${a["scores"][4]} pts</div><div class="results_scoreDetails__rvWSm">${a["guesses"][4]["distance"]["miles"]["amount"]} ${a["guesses"][4]["distance"]["miles"]["unit"]} - ${Math.floor(a["guesses"][4]["time"]/60)==0?"":Math.floor(a["guesses"][4]["time"]/60)} ${Math.floor(a["guesses"][4]["time"]/60)==0?"":"min,"} ${a["guesses"][4]["time"]%60} sec</div></div><div class="results_column__BTeok"><div class="results_score__jUqyZ">${a["totscore"]} pts</div><div class="results_scoreDetails__rvWSm">${a["totDist"]} km</div></div></div>`);
    
            
            $("#bsbBody").append(`<div class="results_rowDivider__py9ZY"></div>`)
            // When I click the div I open game overview in a new tab
            // I can’t show it on this page because I can’t add the the native event listener to the new div because it’s disgustingly obfuscated 
            $(`#bsbEntry${i}`).click(function (e) {
                e.preventDefault();
                window.open(`https://www.geoguessr.com/results/${a["gametoken"]}`);
            });
        }
    }
}

/*****waitforkeyelements.js Used to combat no page refresh due to AJAX. *
 *      @Author BrockA
 * /
/*--- waitForKeyElements():  A utility function, for Greasemonkey scripts,
    that detects and handles AJAXed content.
    
    Usage example:

        waitForKeyElements (
            "div.comments"
            , commentCallbackFunction
        );

        //--- Page-specific function to do what we want when the node is found.
        function commentCallbackFunction (jNode) {
            jNode.text ("This comment changed by waitForKeyElements().");
        }

    IMPORTANT: This function requires your script to have loaded jQuery.
*/
function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = $(selectorTxt);
    else
        targetNodes     = $(iframeSelector).contents ()
                                           .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        /*--- Found target node(s).  Go through each and act if they
            are new.
        */
        targetNodes.each ( function () {
            var jThis        = $(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = waitForKeyElements.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey]
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    waitForKeyElements (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址