Multi rating graph for Codeforces by im0qianqian (http & https)

Enable to show rating history graph with other accounts on profile pages at Codeforces

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name           Multi rating graph for Codeforces by im0qianqian (http & https)
// @namespace      https://www.dreamwings.cn/
// @description    Enable to show rating history graph with other accounts on profile pages at Codeforces
// @license        http://creativecommons.org/publicdomain/zero/1.0/
// @copyright      im0qianqian
// @version        1.5
// @match        *://www.codeforces.com/profile/*
// @match        *://codeforces.com/profile/*
// @match        *://www.codeforces.ru/profile/*
// @match        *://codeforces.ru/profile/*
// ==/UserScript==

// v1.5  2021/08/06 (by HamletPetrosyan, yak_ex)Fix failures related to codeforces rating graph update
//                  Add setting variables
// v1.4  2019/01/03 (by im0qianqian) Add support for https.
// v1.3  2015/05/05 Sync with site changes.
// v1.2  2014/06/05 Autocomplete account names
//                  Keep the color of the highest dots
// v1.1  2013/03/15 Fix failure to get log-in account
//                  Version jump because Chrome recognizes 0.0x as 1.0
// v0.03 2011/04/17 Show log-in account always
// v0.02 2011/04/16 Adjust yaxis scale
//                  Warn if data can't be obtained
// v0.01 2011/04/16 Initial version

///////////////////////////////////////////////////////////////////////
//
// The following part is executed in content page scope
//

// ------------ Setting variables ------------

var highest_rating = false;         // always shows the highest user ratings 
var my_plot_by_default = false;     // always shows your rating graph in other people's profile pages

//

function extract_data(cont) {
    var re1 = new RegExp('data\\.push\\(([\\S\\s]*?)\\);\\s*data\\.push\\(([\\S\\s]*?)\\);', 'm');
    return re1.test(cont) ? [RegExp.$1, RegExp.$2] : undefined;
}

function extract_scale(cont) {
    var re2 = new RegExp('yaxis: \\{(\r\n|\r|\n)            min: (\\d+),(\r\n|\r|\n)            max: (\\d+),');
    return re2.test(cont) ? [RegExp.$2, RegExp.$4] : undefined;
}

function extract_scaleX(cont) {
    var re3 = new RegExp('zoomRange: \\[172800000, null\\],(\r\n|\r|\n)            panRange: \\[(\\d+), (\\d+)\\]');
    return re3.test(cont) ? [RegExp.$2, RegExp.$3] : undefined;
}

function get_account_data(id) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://' + window.location.host + '/profile/' + id, false);
    xhr.send(null);
    if (xhr.status == 200) {
        return [extract_data(xhr.responseText), extract_scale(xhr.responseText), extract_scaleX(xhr.responseText)];
    }
    return undefined;
}

function update_graph(input) {
    if (input == null) return;
    var handle = window.location.href.match(/[^/]*$/);
    input = handle + ' ' + input;
    accounts = input.trim().split(' ');
    var check = {};
    data = new Array();
    datas = [];
    var mymin = 10000, mymax = -1, myXmin = 10000000000000, myXmax = -1;
    var idx = 0;
    for (var i = 0; i < accounts.length; ++i) {
        if (accounts[i] != '' && check[accounts[i]] == undefined) {
            check[accounts[i]] = 1;
            var d = get_account_data(accounts[i]);
            if (d != undefined && d[0] != undefined) {
                data.push(eval(d[0][0]));

                if (highest_rating || accounts.length == 1) {
                    data.push(eval(d[0][1]));
                    datas[2 * idx] = { label: accounts[i], data: data[2 * idx] };
                    datas[2 * idx + 1] = { clickable: false, hoverable: false, color: "red", data: data[2 * idx + 1] };
                }
                else {
                    datas[idx] = { label: accounts[i], data: data[idx] };
                }

                ++idx;
                if (d[1] != undefined) {
                    if (d[1][0] < mymin) mymin = d[1][0];
                    if (d[1][1] > mymax) mymax = d[1][1];
                }
                if (d[2] != undefined) {
                    if (d[2][0] < myXmin) myXmin = d[2][0];
                    if (d[2][1] > myXmax) myXmax = d[2][1];
                }
            } else {
                alert("Can't get information for account: " + accounts[i] + ".");
            }
        }
    }
    if (idx == 1) {
        options.legend.position = "ne";
    } else {
        options.legend.position = "se";
    }
    if (myXmin != 10000000000000 && myXmax != -1) {
        options.xaxis.panRange = [myXmin, myXmax];
    }
    if (mymin != 10000 && mymax != -1) {
        options.yaxis.min = mymin;
        options.yaxis.max = mymax;
        options.yaxis.panRange = [mymin, mymax];
    }
    plot = $.plot($("#usersRatingGraphPlaceholder"), datas, options);
    $("#usersRatingGraphPlaceholder .legend").unbind("click");
    $("#usersRatingGraphPlaceholder .legend").bind("click", account_manage);
}

function account_manage() {
    var handle = window.location.href.match(/[^/]*$/);
    var dialog = $('<div id="account-dialog"/>').css({
        position: 'fixed', padding: '5px', width: '30em', zIndex: 2000, left: '50%', top: '50%', marginTop: '-3.5em', marginLeft: '-15em',
        border: '1px solid', borderRadius: '5px', background: 'rgb(255,255,255)', boxShadow: 'rgb(64,64,64) 5px 5px 5px'
    }).html(
        '<p>Input space-separated accounts without this account.</p>' +
        '<form id="account-form"><p><input type="text" id="accounts" size="40" value="' + ((handle != login_account && my_plot_by_default == true) ? login_account : '') + '"></p>' +
        '<p><input type="submit" id="ok" value="OK"> <input type="button" id="cancel" value="cancel"></p></form>'
    );
    $('p', dialog).css({ margin: '1em' });
    $('#cancel', dialog).click(function () {
        $('#account-dialog').remove();
        $('#account-dialog-blocker').remove();
    });
    $('#account-form', dialog).submit(function () {
        var input = $('#accounts').val();
        $('#account-dialog').remove();
        $('#account-dialog-blocker').remove();
        update_graph(input);
        return false;
    }).keydown(function (e) {
        if (e.keyCode == 27) {
            $('#cancel').click();
        }
    });
    var blocker = $('<div id="account-dialog-blocker"/>').css({
        position: 'fixed', top: 0, left: 0, bottom: 0, right: 0, width: '100%', height: '100%', zIndex: 15,
        background: 'rgb(64,64,64)', opacity: 0.75
    });
    $('body').append(blocker);
    $('body').append(dialog);
    $('#accounts').autocomplete("/data/handles", {
        delay: 200,
        width: 200,
        selectFirst: false,
        matchContains: true,
        multiple: true,
        multipleSeparator: ' ',
        minChars: 3,
        scroll: true,
    });
    $('#accounts').focus();
}

///////////////////////////////////////////////////////////////////////
//
// The following part is executed in userjs scope.
//

function add_unbind(cont) {
    var marker = '$("#usersRatingGraphPlaceholder").bind("plothover"';
    return cont.replace(marker, '$("#usersRatingGraphPlaceholder").unbind("plothover");\n' + marker);
}

function get_login_account() {
    var e = document.getElementById('header');
    var re3 = new RegExp('<a href="/profile/([^"]*)">[^<]*</a>[^<]*<a href="[^"]*/logout">');
    return re3.test(e.innerHTML) ? RegExp.$1 : undefined;
}

function disable_default_plot(cont) {
    return cont.replace('var plot = $.plot($("#usersRatingGraphPlaceholder"), datas, options);', '').replace('var ctx = plot.getCanvas().getContext("2d");', '');
}

function add_account_manage(cont) {
    var marker = 'var prev = -1;';
    var target = '';
    target += 'var highest_rating = ' + highest_rating + ';\n';
    target += 'var my_plot_by_default = ' + my_plot_by_default + ';\n';
    target += 'var extract_data = ' + extract_data + ';\n';
    target += 'var extract_scale = ' + extract_scale + ';\n';
    target += 'var extract_scaleX = ' + extract_scaleX + ';\n';
    target += 'var get_account_data = ' + get_account_data + ';\n';
    var login_account = get_login_account();
    if (login_account != undefined) {
        target += 'var login_account = "' + get_login_account() + '";\n';
    } else {
        target += 'var login_account = "";\n';
    }
    target += 'options.legend = {};\n';
    target += 'var account_manage;\n';
    target += 'var update_graph = ' + update_graph + ';\n';
    target += 'account_manage = ' + account_manage + ';\n';
    target += 'if(my_plot_by_default) update_graph(login_account);\n';
    target += '$("#usersRatingGraphPlaceholder .legend").unbind("click");\n';
    target += '$("#usersRatingGraphPlaceholder .legend").bind("click", account_manage);\n';
    // CAUTION FRAGILE: monkey patch for Autocompleter to handle multiple words correctly
    target += '$(function() {\n';
    target += 'var old = $.Autocompleter;\n';
    target += 'eval("$.Autocompleter = " + (""+$.Autocompleter).replace("currentValue == q", "lastWord(currentValue) == q"));\n';
    target += '$.Autocompleter.defaults = old.defaults;$.Autocompleter.Cache = old.Cache;$.Autocompleter.Select = old.Select;\n';
    target += '});\n';

    return cont.replace(marker, target + marker);
}

function get_target_script() {
    var e = document.getElementById('pageContent').getElementsByTagName('script');
    for (var i = 0; i < e.length; ++i) {
        if (e[i].textContent.match(/data\.push/) != null) {
            return e[i];
        }
    }
}

script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = disable_default_plot(add_account_manage(add_unbind(get_target_script().textContent)));

document.body.appendChild(script);
document.body.removeChild(script);