Greasy Fork is available in English.

AtCoder Copy Contest ID

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

2023-04-27 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  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.4
  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. @media (max-width: 1150px) {
  37. .contest-title {
  38. width: auto;
  39. max-width: 580px;
  40. overflow: hidden;
  41. white-space: nowrap;
  42. text-overflow: ellipsis;
  43. }
  44. #copy-button-text {
  45. display: none;
  46. }
  47. }
  48. @media (max-width: 1000px) {
  49. .contest-title {
  50. width: auto;
  51. max-width: 300px;
  52. overflow: hidden;
  53. white-space: nowrap;
  54. text-overflow: ellipsis;
  55. }
  56. }
  57. `;
  58.  
  59. const addCopyButton = () => {
  60. if (!navbar) {
  61. console.error("Failed to find navbar element.");
  62. return;
  63. }
  64. navbar.insertAdjacentHTML(
  65. "beforeend",
  66. `
  67. <li>
  68. <a id="copy-contest-id-button" style="cursor: pointer; font-size: 12px">
  69. <span class="glyphicon ${ICONS.COPY}" style="margin-right: 2px" aria-hidden="true"></span>
  70. <span id="copy-button-text">${INIT_LABEL}</span>
  71. </a>
  72. </li>
  73. `
  74. );
  75. };
  76.  
  77. const setupCopyButton = () => {
  78. const button = $("#copy-contest-id-button");
  79. const icon = $(".glyphicon", button);
  80. const label = $("#copy-button-text");
  81. let isCopying = false;
  82.  
  83. const resetButton = () => {
  84. icon.classList.replace(ICONS.OK, ICONS.COPY, ICONS.REMOVE);
  85. label.textContent = INIT_LABEL;
  86. isCopying = false;
  87. };
  88.  
  89. const copyToClipboard = async () => {
  90. if (isCopying) return;
  91. isCopying = true;
  92.  
  93. try {
  94. await GM_setClipboard(getContestId(), {
  95. type: "text",
  96. mimetype: "text/plain",
  97. });
  98. icon.classList.replace(ICONS.COPY, ICONS.OK);
  99. label.textContent = COPIED_LABEL;
  100. } catch (error) {
  101. console.error(`Failed to copy contest ID: ${error}`);
  102. icon.classList.replace(ICONS.COPY, ICONS.REMOVE);
  103. label.textContent = FAILED_LABEL;
  104. } finally {
  105. setTimeout(resetButton, 1800);
  106. }
  107. };
  108.  
  109. button.addEventListener("click", copyToClipboard);
  110. };
  111.  
  112. document.head.appendChild(style);
  113. addCopyButton();
  114. setupCopyButton();
  115. };
  116.  
  117. copyContestId();