AtCoder Copy Contest ID

Add a button to copy the contest ID to the clipboard in AtCoder contest pages

Verze ze dne 16. 04. 2023. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name AtCoder Copy Contest ID
  3. // @name:ja AtCoder Copy Contest ID
  4. // @namespace https://github.com/xe-o
  5. // @version 0.3
  6. // @description Add a button to copy the contest ID to the clipboard in AtCoder contest pages
  7. // @description:ja AtCoderコンテストページのナビゲーションバーへ、コンテストIDをコピーするためのボタンを追加します
  8. // @author XERO
  9. // @license MIT
  10. // @match https://atcoder.jp/*
  11. // @grant GM_setClipboard
  12. // @run-at document-idle
  13. // ==/UserScript==
  14.  
  15. const INIT_LABEL = "Copy Contest ID";
  16. const COPIED_LABEL = "Copied!";
  17. const FAILED_LABEL = "Failed to copy";
  18. const ICONS = {
  19. COPY: "glyphicon-copy",
  20. OK: "glyphicon-ok",
  21. REMOVE: "glyphicon-remove",
  22. };
  23.  
  24. const copyContestId = () => {
  25. const $ = (selector, baseElement = document) =>
  26. baseElement.querySelector(selector);
  27. const getContestId = () => window.location.pathname.split("/")[2];
  28. const navbar = $(".navbar-nav");
  29. const style = document.createElement("style");
  30. style.innerHTML = `
  31. @media (max-width: 991px) {
  32. .contest-title {
  33. width: auto;
  34. }
  35. }
  36. `;
  37.  
  38. const addCopyButton = () => {
  39. if (!navbar) {
  40. console.error("Failed to find navbar element.");
  41. return;
  42. }
  43. navbar.insertAdjacentHTML(
  44. "beforeend",
  45. `
  46. <li>
  47. <a id="copy-contest-id-button" style="cursor: pointer; font-size: 12px">
  48. <span class="glyphicon ${ICONS.COPY}" style="margin-right: 2px" aria-hidden="true"></span>
  49. <span id="copy-button-text">${INIT_LABEL}</span>
  50. </a>
  51. </li>
  52. `
  53. );
  54. };
  55.  
  56. const setupCopyButton = () => {
  57. const button = $("#copy-contest-id-button");
  58. const icon = $(".glyphicon", button);
  59. const label = $("#copy-button-text");
  60. let isCopying = false;
  61.  
  62. const resetButton = () => {
  63. icon.classList.replace(ICONS.OK, ICONS.COPY, ICONS.REMOVE);
  64. label.textContent = INIT_LABEL;
  65. isCopying = false;
  66. };
  67.  
  68. const copyToClipboard = async () => {
  69. if (isCopying) return;
  70. isCopying = true;
  71.  
  72. try {
  73. await GM_setClipboard(getContestId(), {
  74. type: "text",
  75. mimetype: "text/plain",
  76. });
  77. icon.classList.replace(ICONS.COPY, ICONS.OK);
  78. label.textContent = COPIED_LABEL;
  79. } catch (error) {
  80. console.error(`Failed to copy contest ID: ${error}`);
  81. icon.classList.replace(ICONS.COPY, ICONS.REMOVE);
  82. label.textContent = FAILED_LABEL;
  83. } finally {
  84. setTimeout(resetButton, 1800);
  85. }
  86. };
  87.  
  88. button.addEventListener("click", copyToClipboard);
  89. };
  90.  
  91. document.head.appendChild(style);
  92. addCopyButton();
  93. setupCopyButton();
  94. };
  95.  
  96. copyContestId();