AtCoder Non-Participant Hider

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

Fra 16.07.2023. Se den seneste versjonen.

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