AtCoder Contest Standings with Twitter ID

Display Twitter IDs in contest standings.

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

  1. // ==UserScript==
  2. // @name AtCoder Contest Standings with Twitter ID
  3. // @namespace https://twitter.com/KakurenboUni
  4. // @version 1.0.0
  5. // @match https://atcoder.jp/contests/*/standings*
  6. // @description Display 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. const cache = new Map();
  15. const STORAGE_ID_PREFIX = "AtCoder-Contest-Standings-with-Twitter-ID"
  16.  
  17.  
  18. window.onload = async function() {
  19. await rendered();
  20.  
  21. const $checkboxes = Array.from(document.querySelectorAll("div.checkbox"));
  22.  
  23. const $newElement = document.createElement("div");
  24.  
  25. $newElement.innerHTML = `<label><input id="checkbox-twitter" type="checkbox"> Twitter ID を表示</label>`;
  26. $newElement.className = "checkbox";
  27.  
  28. $checkboxes[2].after($newElement);
  29.  
  30. const $new_checkbox = document.querySelector("input#checkbox-twitter");
  31. $new_checkbox.checked = localStorage.getItem(`${STORAGE_ID_PREFIX}.enable`) === "true";
  32.  
  33. setVisuality();
  34.  
  35. if(isAvailable() && $new_checkbox.checked) display();
  36. $new_checkbox.addEventListener('click', ({ target : { checked } }) => {
  37. if(checked) display();
  38. else hide();
  39. });
  40.  
  41. $checkboxes.at(-1).addEventListener("click", setVisuality);
  42. document.querySelectorAll(".standings-per-page").forEach(element => {
  43. element.addEventListener("click", setVisuality);
  44. })
  45.  
  46. function isAvailable() {
  47. return +document.querySelector("a.standings-per-page.selected").textContent <= 50 || $checkboxes.at(-1).querySelector("input").checked
  48. }
  49. function setVisuality() {
  50. if(isAvailable()) $new_checkbox.disabled = false;
  51. else { $new_checkbox.checked = false; $new_checkbox.disabled = true; }
  52. }
  53. };
  54.  
  55. function display() {
  56. const $usernames = document.querySelectorAll(".standings-rank + .standings-username .username")
  57. $usernames.forEach(async ($username) => {
  58. const username = $username.textContent;
  59. const existing = cache.get(username);
  60. let twitterId = "";
  61. if(existing) {
  62. twitterId = existing;
  63. } else {
  64. const response = await fetch($username.href);
  65. const row_text = await response.text();
  66. const twitterInfomation = row_text.match(/href='\/\/twitter.com\/(.*)'/);
  67. if(!twitterInfomation) return;
  68. twitterId = twitterInfomation[1];
  69. cache.set(username, twitterId)
  70. }
  71. const $span = document.createElement("span");
  72. $span.className = "twitter-link";
  73. $span.style["margin-left"] = "5px"
  74. $span.style["margin-right"] = "2px"
  75. $span.innerHTML = `<a href="https://twitter.com/@${twitterId}" target="_blank" rel="noopener noreferrer">@${twitterId}</a>`
  76. $username.after($span);
  77. })
  78. localStorage.setItem(`${STORAGE_ID_PREFIX}.enable`, true);
  79. }
  80.  
  81. function hide() {
  82. const $targets = document.querySelectorAll(".twitter-link")
  83. $targets.forEach($target => $target.remove());
  84. localStorage.setItem(`${STORAGE_ID_PREFIX}.enable`, false);
  85. }
  86.  
  87. async function rendered() {
  88. let timer;
  89. await new Promise((resolve) => {
  90. observer();
  91. function observer() {
  92. if(document.querySelector("div.checkbox")) {
  93. resolve();
  94. }
  95. timer = setTimeout(observer, 10);
  96. };
  97. });
  98. clearTimeout(timer)
  99. }
  100. }();