AtCoder Non-Participant Hider

Add a filter to hide non-participants from favs only standings table on AtCoder.

Verze ze dne 12. 07. 2023. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name AtCoder Non-Participant Hider
  3. // @namespace https://github.com/Gai-H/atcoder-non-participant-hider
  4. // @version 1.0
  5. // @description Add a filter to hide non-participants from favs only standings table on AtCoder.
  6. // @description:ja お気に入りの順位表に不参加者を隠すフィルターを追加します。
  7. // @author Gai
  8. // @homepage https://github.com/Gai-H
  9. // @source https://github.com/Gai-H/atcoder-non-participant-hider
  10. // @match https://atcoder.jp/*standings*
  11. // @exclude https://atcoder.jp/*standings/json
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. const CHECKBOX_ID = "checkbox-fav-only-participant-atcodernonparticipanthider";
  16. const CHECKBOX_TEXT_JA = "お気に入りの不参加者を非表示";
  17. const CHECKBOX_TEXT_EN = "Hide non-participants from favs";
  18. const CHECKBOX_HTML = `
  19. <div class="checkbox">
  20. <label>
  21. <input id="${CHECKBOX_ID}" type="checkbox">
  22. ${document.cookie.includes("language=ja") ? CHECKBOX_TEXT_JA : CHECKBOX_TEXT_EN}
  23. </label>
  24. </div>
  25. `;
  26.  
  27. let theCheckbox = null;
  28. let favOnlyCheckbox = null;
  29. let standingsTbody = null;
  30.  
  31. // チェックボックス追加
  32. const bodyObserver = new MutationObserver((_, observer) => {
  33. favOnlyCheckbox = document.getElementById("checkbox-fav-only");
  34. if (!favOnlyCheckbox) return;
  35.  
  36. observer.disconnect();
  37. favOnlyCheckbox.addEventListener("change", onFavOnlyCheckboxChange);
  38.  
  39. const favOnlyCheckboxDiv = favOnlyCheckbox.parentElement.parentElement;
  40. favOnlyCheckboxDiv.insertAdjacentHTML("afterend", CHECKBOX_HTML);
  41.  
  42. theCheckbox = document.getElementById(CHECKBOX_ID);
  43. theCheckbox.disabled = !favOnlyCheckbox.checked;
  44. theCheckbox.checked = localStorage.getItem(CHECKBOX_ID) ?? true;
  45. theCheckbox.addEventListener("change", onTheCheckboxChange);
  46.  
  47. standingsTbody = document.getElementById("standings-tbody");
  48. standingsTbodyObserver.observe(standingsTbody, {
  49. childList: true,
  50. attributeFilter: ["class"],
  51. });
  52.  
  53. hideNonParticipants();
  54. });
  55. bodyObserver.observe(document.body, {
  56. childList: true,
  57. subtree: true,
  58. });
  59.  
  60. // 順位表テーブルのハンドラ
  61. const standingsTbodyObserver = new MutationObserver(() => hideNonParticipants());
  62.  
  63. // "お気に入りのみ表示"チェックボックスのハンドラ
  64. const onFavOnlyCheckboxChange = () => {
  65. theCheckbox.disabled = !favOnlyCheckbox.checked;
  66. hideNonParticipants();
  67. };
  68.  
  69. // "お気に入りの不参加者を非表示"チェックボックスのハンドラ
  70. const onTheCheckboxChange = () => {
  71. localStorage.setItem(CHECKBOX_ID, theCheckbox.checked);
  72. hideNonParticipants();
  73. };
  74.  
  75. const hideNonParticipants = () => {
  76. if (!favOnlyCheckbox.checked) return;
  77. const standingsTrs = standingsTbody.children;
  78. for (const row of standingsTrs) {
  79. const rankTd = row.children[0];
  80. if (rankTd.innerText !== "-") continue;
  81. if (theCheckbox.checked) {
  82. row.classList.add("hidden");
  83. } else {
  84. row.classList.remove("hidden");
  85. }
  86. }
  87. };