AtCoder Contest Standings with Twitter ID

Displays Twitter IDs in contest standings.

当前为 2022-06-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AtCoder Contest Standings with Twitter ID
  3. // @namespace https://twitter.com/KakurenboUni
  4. // @version 1.0.2
  5. // @match https://atcoder.jp/contests/*/standings*
  6. // @description Displays Twitter IDs in contest standings.
  7. // @author uni-kakurenbo
  8. // @license MIT
  9. // @supportURL https://twitter.com/KakurenboUni
  10. // ==/UserScript==
  11.  
  12. void function() {
  13. "use strict";
  14.  
  15. // ac-predictor などと併用すると,列の一部が見切れてしまうことがあります.
  16. // その場合は以下の行をコメントアウトし,順位表の幅を広げてください.(サイズは任意に変更していただいて構いません.)
  17. // document.querySelector("#main-container").style.width = "1270px"
  18.  
  19. const cache = new Map();
  20. const STORAGE_ID_PREFIX = "AtCoder-Contest-Standings-with-Twitter-ID"
  21.  
  22.  
  23. window.onload = async function() {
  24. await rendered();
  25.  
  26. const $checkboxes = Array.from(document.querySelectorAll("div.checkbox"));
  27.  
  28. const $outerDivElement = document.createElement("div");
  29.  
  30. $outerDivElement.innerHTML = `<label><input id="checkbox-twitter" type="checkbox"> Twitter ID を表示</label>`;
  31. $outerDivElement.className = "checkbox";
  32.  
  33. $checkboxes[2].after($outerDivElement);
  34.  
  35. const $addedCheckbox = document.querySelector("input#checkbox-twitter");
  36. $addedCheckbox.checked = localStorage.getItem(`${STORAGE_ID_PREFIX}.enable`) === "true";
  37.  
  38. setVisuality();
  39.  
  40. if(isAvailable() && $addedCheckbox.checked) display();
  41. $addedCheckbox.addEventListener('click', ({ target : { checked } }) => {
  42. if(checked) display();
  43. else hide();
  44. });
  45.  
  46. $checkboxes.at(-1).addEventListener("click", setVisuality);
  47. document.querySelectorAll(".standings-per-page").forEach(element => {
  48. element.addEventListener("click", setVisuality);
  49. })
  50.  
  51. function isAvailable() {
  52. return +document.querySelector("a.standings-per-page.selected").textContent <= 50 || $checkboxes.at(-1).querySelector("input").checked
  53. }
  54. function setVisuality() {
  55. if(isAvailable()) $addedCheckbox.disabled = false;
  56. else { $addedCheckbox.checked = false; $addedCheckbox.disabled = true; }
  57. }
  58. };
  59.  
  60. function display() {
  61. const $usernames = document.querySelectorAll(".standings-rank + .standings-username .username")
  62. $usernames.forEach(async ($username) => {
  63. const username = $username.textContent;
  64. const existing = cache.get(username);
  65. let twitterId = "";
  66. if(existing) {
  67. twitterId = existing;
  68. } else {
  69. const response = await fetch($username.href);
  70. const row_text = await response.text();
  71. const twitterInfomation = row_text.match(/href='\/\/twitter.com\/(.*)'/);
  72. if(!twitterInfomation) return;
  73. twitterId = twitterInfomation[1];
  74. cache.set(username, twitterId)
  75. }
  76. const $span = document.createElement("span");
  77. $span.className = "twitter-link";
  78. $span.style["margin-left"] = "5px"
  79. $span.style["margin-right"] = "2px"
  80. $span.innerHTML = `<a href="https://twitter.com/@${twitterId}" target="_blank" rel="noopener noreferrer">@${twitterId}</a>`
  81. $username.after($span);
  82. })
  83. localStorage.setItem(`${STORAGE_ID_PREFIX}.enable`, true);
  84. }
  85.  
  86. function hide() {
  87. const $targets = document.querySelectorAll(".twitter-link")
  88. $targets.forEach($target => $target.remove());
  89. localStorage.setItem(`${STORAGE_ID_PREFIX}.enable`, false);
  90. }
  91.  
  92. async function rendered() {
  93. let timer;
  94. await new Promise((resolve) => {
  95. observer();
  96. function observer() {
  97. if(document.querySelector("div.checkbox")) {
  98. resolve();
  99. }
  100. timer = setTimeout(observer, 10);
  101. };
  102. });
  103. clearTimeout(timer)
  104. }
  105. }();