HamletPetrosyan

multi_rating_graph_cf.user

Aug 6th, 2021 (edited)
162
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name           Multi rating graph for Codeforces
  3. // @namespace      https://yak2.myhome.cx/
  4. // @description    Enable to show rating history graph with other accounts on profile pages at Codeforces
  5. // @license        https://creativecommons.org/publicdomain/zero/1.0/
  6. // @copyright      yak_ex
  7. // @version        1.4
  8. // @include        https://www.codeforces.com/profile/*
  9. // @include        https://codeforces.com/profile/*
  10. // @include        https://www.codeforces.ru/profile/*
  11. // @include        https://codeforces.ru/profile/*
  12. // ==/UserScript==
  13.  
  14. // v1.4  2021/08/06 (by HamletPetrosyan)Fix failures related to codeforces rating graph update
  15. //                  Add setting variables
  16. // v1.3  2015/05/05 Sync with site changes.
  17. // v1.2  2014/06/05 Autocomplete account names
  18. //                  Keep the color of the highest dots
  19. // v1.1  2013/03/15 Fix failure to get log-in account
  20. //                  Version jump because Chrome recognizes 0.0x as 1.0
  21. // v0.03 2011/04/17 Show log-in account always
  22. // v0.02 2011/04/16 Adjust yaxis scale
  23. //                  Warn if data can't be obtained
  24. // v0.01 2011/04/16 Initial version
  25.  
  26. ///////////////////////////////////////////////////////////////////////
  27. //
  28. // The following part is executed in content page scope
  29. //
  30.  
  31. // ------------ Setting variables ------------
  32.  
  33. var highest_rating = false;         // always shows the highest user ratings
  34. var my_plot_by_default = false;     // always shows your rating graph in other people's profile pages
  35.  
  36. //
  37.  
  38. function extract_data(cont)
  39. {
  40.     var re1 = new RegExp('data\\.push\\(([\\S\\s]*?)\\);\\s*data\\.push\\(([\\S\\s]*?)\\);', 'm');
  41.     return re1.test(cont) ? [RegExp.$1, RegExp.$2] : undefined;
  42. }
  43.  
  44. function extract_scale(cont)
  45. {
  46.     var re2 = new RegExp('yaxis: \\{(\r\n|\r|\n)            min: (\\d+),(\r\n|\r|\n)            max: (\\d+),');
  47.     return re2.test(cont) ? [RegExp.$2, RegExp.$4] : undefined;
  48. }
  49.  
  50. function extract_scaleX(cont)
  51. {
  52.     var re3 = new RegExp('zoomRange: \\[172800000, null\\],(\r\n|\r|\n)            panRange: \\[(\\d+), (\\d+)\\]');
  53.     return re3.test(cont) ? [RegExp.$2, RegExp.$3] : undefined;
  54. }
  55.  
  56. function get_account_data(id)
  57. {
  58.     var xhr = new XMLHttpRequest();
  59.     xhr.open('GET', 'https://' + window.location.host + '/profile/' + id, false);
  60.     xhr.send(null);
  61.     if(xhr.status == 200) {
  62.         return [extract_data(xhr.responseText), extract_scale(xhr.responseText), extract_scaleX(xhr.responseText)];
  63.     }
  64.     return undefined;
  65. }
  66.  
  67. function update_graph(input)
  68. {
  69.     if(input == null) return;
  70.     var handle = window.location.href.match(/[^/]*$/);
  71.     input = handle + ' ' + input;
  72.     accounts = input.trim().split(' ');
  73.     var check = {};
  74.     data = new Array();
  75.     datas = [];
  76.     var mymin = 10000, mymax = -1, myXmin = 10000000000000, myXmax = -1;
  77.     var idx = 0;
  78.     for(var i = 0; i < accounts.length; ++i) {
  79.         if(accounts[i] != '' && check[accounts[i]] == undefined) {
  80.             check[accounts[i]] = 1;
  81.             var d = get_account_data(accounts[i]);
  82.             if(d != undefined && d[0] != undefined) {
  83.                 data.push(eval(d[0][0]));
  84.  
  85.                 if (highest_rating || accounts.length == 1) {
  86.                     data.push(eval(d[0][1]));
  87.                     datas[2*idx] = { label: accounts[i], data: data[2*idx] };
  88.                     datas[2*idx+1] = { clickable: false, hoverable: false, color: "red", data: data[2*idx+1] };
  89.                 }
  90.                 else {
  91.                     datas[idx] = { label: accounts[i], data: data[idx] };
  92.                 }
  93.                
  94.                 ++idx;
  95.                 if(d[1] != undefined) {
  96.                     if(d[1][0] < mymin) mymin = d[1][0];
  97.                     if(d[1][1] > mymax) mymax = d[1][1];
  98.                 }
  99.                 if(d[2] != undefined) {
  100.                     if(d[2][0] < myXmin) myXmin = d[2][0];
  101.                     if(d[2][1] > myXmax) myXmax = d[2][1];
  102.                 }
  103.             } else {
  104.                 alert("Can't get information for account: " + accounts[i] + ".");
  105.             }
  106.         }
  107.     }
  108.     if(idx == 1) {
  109.         options.legend.position = "ne";
  110.     } else {
  111.         options.legend.position = "se";
  112.     }
  113.     if(myXmin != 10000000000000 && myXmax != -1){
  114.         options.xaxis.panRange = [myXmin, myXmax]; 
  115.     }
  116.     if(mymin != 10000 && mymax != -1){
  117.         options.yaxis.min = mymin;
  118.         options.yaxis.max = mymax;
  119.         options.yaxis.panRange = [mymin, mymax];
  120.     }
  121.     plot = $.plot($("#usersRatingGraphPlaceholder"), datas, options);
  122.     $("#usersRatingGraphPlaceholder .legend").unbind("click");
  123.     $("#usersRatingGraphPlaceholder .legend").bind("click", account_manage);
  124. }
  125.  
  126. function account_manage()
  127. {
  128.     var handle = window.location.href.match(/[^/]*$/);
  129.     var dialog = $('<div id="account-dialog"/>').css({
  130.         position:'fixed',padding:'5px',width:'30em',zIndex:2000,left:'50%',top:'50%',marginTop:'-3.5em',marginLeft:'-15em',
  131.         border:'1px solid', borderRadius:'5px',background:'rgb(255,255,255)',boxShadow:'rgb(64,64,64) 5px 5px 5px'
  132.     }).html(
  133.         '<p>Input space-separated accounts without this account.</p>' +
  134.         '<form id="account-form"><p><input type="text" id="accounts" size="40" value="'+((handle != login_account && my_plot_by_default == true) ? login_account : '')+'"></p>' +
  135.         '<p><input type="submit" id="ok" value="OK"> <input type="button" id="cancel" value="cancel"></p></form>'
  136.     );
  137.     $('p', dialog).css({margin:'1em'});
  138.     $('#cancel', dialog).click(function() {
  139.         $('#account-dialog').remove();
  140.         $('#account-dialog-blocker').remove();
  141.     });
  142.     $('#account-form', dialog).submit(function() {
  143.         var input = $('#accounts').val();
  144.         $('#account-dialog').remove();
  145.         $('#account-dialog-blocker').remove();
  146.         update_graph(input);
  147.         return false;
  148.     }).keydown(function(e) {
  149.         if(e.keyCode == 27) {
  150.             $('#cancel').click();
  151.         }
  152.     });
  153.     var blocker = $('<div id="account-dialog-blocker"/>').css({
  154.         position:'fixed',top:0,left:0,bottom:0,right:0,width:'100%',height:'100%',zIndex:15,
  155.         background:'rgb(64,64,64)',opacity:0.75
  156.     });
  157.     $('body').append(blocker);
  158.     $('body').append(dialog);
  159.     $('#accounts').autocomplete("/data/handles", {
  160.         delay: 200,
  161.         width: 200,
  162.         selectFirst: false,
  163.         matchContains: true,
  164.         multiple: true,
  165.         multipleSeparator: ' ',
  166.         minChars: 3,
  167.         scroll: true,
  168.     });
  169.     $('#accounts').focus();
  170. }
  171.  
  172. ///////////////////////////////////////////////////////////////////////
  173. //
  174. // The following part is executed in userjs scope.
  175. //
  176.  
  177. function add_unbind(cont)
  178. {
  179.     var marker = '$("#usersRatingGraphPlaceholder").bind("plothover"';
  180.     return cont.replace(marker, '$("#usersRatingGraphPlaceholder").unbind("plothover");\n' + marker);
  181. }
  182.  
  183. function get_login_account()
  184. {
  185.     var e = document.getElementById('header');
  186.     var re3 = new RegExp('<a href="/profile/([^"]*)">[^<]*</a>[^<]*<a href="[^"]*/logout">');
  187.     return re3.test(e.innerHTML) ? RegExp.$1 : undefined;
  188. }
  189.  
  190. function disable_default_plot(cont)
  191. {
  192.     return cont.replace('var plot = $.plot($("#usersRatingGraphPlaceholder"), datas, options);', '').replace('var ctx = plot.getCanvas().getContext("2d");', '');
  193. }
  194.  
  195. function add_account_manage(cont)
  196. {
  197.     var marker = 'var prev = -1;';
  198.     var target = '';
  199.     target += 'var highest_rating = ' + highest_rating + ';\n';
  200.     target += 'var my_plot_by_default = ' + my_plot_by_default + ';\n';
  201.     target += 'var extract_data = ' + extract_data + ';\n';
  202.     target += 'var extract_scale = ' + extract_scale + ';\n';
  203.     target += 'var extract_scaleX = ' + extract_scaleX + ';\n';
  204.     target += 'var get_account_data = ' + get_account_data + ';\n';
  205.     var login_account = get_login_account();
  206.     if(login_account != undefined) {
  207.         target += 'var login_account = "' + get_login_account() + '";\n';
  208.     } else {
  209.         target += 'var login_account = "";\n';
  210.     }
  211.     target += 'options.legend = {};\n';
  212.     target += 'var account_manage;\n';
  213.     target += 'var update_graph = ' + update_graph + ';\n';
  214.     target += 'account_manage = ' + account_manage + ';\n';
  215.     target += 'if(my_plot_by_default) update_graph(login_account);\n';
  216.     target += '$("#usersRatingGraphPlaceholder .legend").unbind("click");\n';
  217.     target += '$("#usersRatingGraphPlaceholder .legend").bind("click", account_manage);\n';
  218. // CAUTION FRAGILE: monkey patch for Autocompleter to handle multiple words correctly
  219.     target += '$(function() {\n';
  220.     target += 'var old = $.Autocompleter;\n';
  221.     target += 'eval("$.Autocompleter = " + (""+$.Autocompleter).replace("currentValue == q", "lastWord(currentValue) == q"));\n';
  222.     target += '$.Autocompleter.defaults = old.defaults;$.Autocompleter.Cache = old.Cache;$.Autocompleter.Select = old.Select;\n';
  223.     target += '});\n';
  224.  
  225.     return cont.replace(marker, target + marker);
  226. }
  227.  
  228. function get_target_script()
  229. {
  230.     var e = document.getElementById('pageContent').getElementsByTagName('script');
  231.     for(var i = 0; i < e.length; ++i) {
  232.         if(e[i].textContent.match(/data\.push/) != null) {
  233.             return e[i];
  234.         }
  235.     }
  236. }
  237.  
  238. script = document.createElement('script');
  239. script.setAttribute("type", "application/javascript");
  240. script.textContent = disable_default_plot(add_account_manage(add_unbind(get_target_script().textContent)));
  241.  
  242. document.body.appendChild(script);
  243. document.body.removeChild(script);
RAW Paste Data