atcoder-standings-lang

AtCoder の順位表に最多提出言語を追加します.uesugi6111 さん作のスクリプトが元ネタです.

Stan na 10-11-2020. Zobacz najnowsza wersja.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

  1. // ==UserScript==
  2. // @name atcoder-standings-lang
  3. // @namespace iilj
  4. // @version 2020.11.10.0
  5. // @description AtCoder の順位表に最多提出言語を追加します.uesugi6111 さん作のスクリプトが元ネタです.
  6. // @author iilj
  7. // @supportURL https://github.com/iilj/atcoder-standings-lang/issues
  8. // @match https://atcoder.jp/contests/*/standings*
  9. // ==/UserScript==
  10.  
  11. /* globals $ */
  12.  
  13. /**
  14. * ユーザID/言語ごとの提出数
  15. * @typedef {Object} UserLangEntry
  16. * @property {string} user_id ユーザ ID
  17. * @property {string} language 言語名
  18. * @property {number} count 提出数
  19. */
  20.  
  21. (() => {
  22. 'use strict';
  23.  
  24. /** @type {Map<string, UserLangEntry[]>} */
  25. const userLangEntryMap = new Map();
  26.  
  27. const fetchLangJson = async () => {
  28. console.log('@kenkooooさんありがとう');
  29. // 2e5 個くらい要素があるのでキャッシュする
  30. const res = await fetch("https://kenkoooo.com/atcoder/resources/lang.json", { cache: 'force-cache' });
  31. /** @type {UserLangEntry[]} */
  32. const userLangEntries = await res.json();
  33.  
  34. // prepare map
  35. userLangEntries.forEach(userLangEntry => {
  36. if (userLangEntryMap.has(userLangEntry.user_id)) {
  37. userLangEntryMap.get(userLangEntry.user_id).push(userLangEntry);
  38. } else {
  39. userLangEntryMap.set(userLangEntry.user_id, [userLangEntry]);
  40. }
  41. });
  42.  
  43. // sort arrays
  44. userLangEntryMap.forEach(userLangArray => {
  45. userLangArray.sort((a, b) => b.count - a.count); // in place
  46. });
  47. };
  48.  
  49. /** @type {(anchor: HTMLAnchorElement) => void} */
  50. const updateAnchor = (anchor) => {
  51. if (!anchor.href.includes('/users/')) return;
  52.  
  53. const user_id = anchor.text.trim();
  54. if (!userLangEntryMap.has(user_id)) return;
  55.  
  56. const userLangArray = userLangEntryMap.get(user_id);
  57.  
  58. const tooltipHtml = userLangArray.map((userLangEntry) =>
  59. `${userLangEntry.language} : ${userLangEntry.count}`
  60. ).join('<br>');
  61.  
  62. let langHtml = userLangArray[0].language;
  63. if (userLangArray.length >= 2) {
  64. langHtml += '<div style="font-size:10px;display:inline;">'
  65. + `/${userLangArray[1].language}`
  66. + (userLangArray.length >= 3 ? `/${userLangArray[2].language}` : '')
  67. + ' </div>';
  68. }
  69.  
  70. anchor.insertAdjacentHTML('beforeend',
  71. '/'
  72. + '<div data-toggle="tooltip" data-html="true" data-placement="right" style="font-size:12px;display:inline;" title="' + tooltipHtml + '">'
  73. + langHtml
  74. + '</div>');
  75. $('[data-toggle="tooltip"]').tooltip();
  76. };
  77.  
  78. /** @type {(tbody: HTMLTableSectionElement) => void} */
  79. const updateTable = (tbody) => {
  80. tbody.querySelectorAll('.username').forEach(anchor => {
  81. updateAnchor(anchor);
  82. });
  83. };
  84.  
  85. /** @type {HTMLTableSectionElement} */
  86. let tbody = null;
  87.  
  88. const tableObserver = new MutationObserver(() => {
  89. updateTable(tbody);
  90. });
  91. const parentObserver = new MutationObserver(async () => {
  92. tbody = document.getElementById('standings-tbody');
  93. if (tbody) {
  94. parentObserver.disconnect();
  95. await fetchLangJson();
  96. updateTable(tbody);
  97. tableObserver.observe(
  98. tbody,
  99. { childList: true }
  100. )
  101. }
  102. });
  103. parentObserver.observe(
  104. document.getElementById('vue-standings'),
  105. { childList: true, subtree: true }
  106. );
  107. })();