AtCoderLanguageButtons

Add buttons to select language.

  1. // ==UserScript==
  2. // @name AtCoderLanguageButtons
  3. // @namespace http://atcoder.jp/
  4. // @version 0.3.0
  5. // @description Add buttons to select language.
  6. // @author magurofly
  7. // @match https://atcoder.jp/contests/*/tasks/*
  8. // @match https://atcoder.jp/contests/*/submit*
  9. // @match https://atcoder.jp/contests/*/custom_test*
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const VERSION = "0.3.0";
  18.  
  19. // 設定があればロード
  20. const config = (() => {
  21. let buttons = new Set();
  22. let currentChoice = null;
  23.  
  24. return {
  25. load() {
  26. const config_json = GM_getValue("config", "null");
  27. if (config_json) {
  28. try {
  29. const data = JSON.parse(config_json);
  30. buttons = new Set(data.buttons);
  31. if (data.choice && !currentChoice) currentChoice = data.choice;
  32. } catch (e) {
  33. console.error("AtCoderLanguageButtons: Invalid JSON", config_json);
  34. }
  35. }
  36. },
  37.  
  38. save() {
  39. const data = { buttons: [...buttons], choice: currentChoice };
  40. const config_json = JSON.stringify(data);
  41. GM_setValue("config", config_json);
  42. console.info("AtCoderLanguageButtons: saved");
  43. },
  44.  
  45. isSet(lang) {
  46. return buttons.has(lang);
  47. },
  48.  
  49. set(lang, flag) {
  50. if (flag) {
  51. buttons.add(lang);
  52. } else {
  53. buttons.delete(lang);
  54. }
  55. },
  56.  
  57. set choice(choice) {
  58. currentChoice = choice;
  59. this.save();
  60. },
  61.  
  62. get choice() {
  63. return currentChoice;
  64. },
  65. };
  66. })();
  67.  
  68. // 表示する
  69. const view = (() => {
  70. const modal = document.createElement("section");
  71. modal.className = "modal fade";
  72. modal.innerHTML = `
  73. <div class="modal-dialog">
  74. <div class="modal-content">
  75. <div class="modal-header">
  76. <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
  77. <h4 class="modal-title">AtCoderLanguageButtons</h4>
  78. </div>
  79. <div class="modal-body">
  80. <p>ボタンを表示する言語を選択してください</p>
  81. <form id="atcoder-language-buttons-config">
  82. <div id="atcoder-language-buttons-config-languages"></div>
  83. </form>
  84. </div>
  85. </div>
  86. </div>
  87. `;
  88.  
  89. const choices = new Map();
  90. const group = document.createElement("span");
  91.  
  92. return {
  93. init() {
  94. this.initView();
  95. this.initConfigWindow();
  96. this.updateButtons();
  97. },
  98.  
  99. initView() {
  100. const container = document.getElementById("select-lang");
  101. container.appendChild(group);
  102.  
  103. const openButton = document.createElement("button");
  104. openButton.type = "button";
  105. openButton.className = "glyphicon glyphicon-cog";
  106. openButton.addEventListener("click", _e => {
  107. this.showConfig();
  108. });
  109. container.appendChild(openButton);
  110.  
  111. const select = $("#select-lang select[name='data.LanguageId']");
  112. select.on("change", _e => {
  113. config.choice = select.val();
  114. this.highlightButtons();
  115. });
  116. if (config.choice && !select.val()) {
  117. select.val(config.choice);
  118. this.highlightButtons();
  119. }
  120. },
  121.  
  122. initConfigWindow() {
  123. document.body.appendChild(modal);
  124. const langs = document.getElementById("atcoder-language-buttons-config-languages");
  125. for (const langOption of document.querySelector("#select-lang select").querySelectorAll("option[value]")) {
  126. const checkbox = document.createElement("input");
  127. checkbox.type = "checkbox";
  128. checkbox.value = langOption.value;
  129. checkbox.addEventListener("change", _e => {
  130. config.set(checkbox.value, checkbox.checked);
  131. config.save();
  132. this.updateButtons();
  133. });
  134. choices.set(langOption.value, {checkbox, label: langOption.textContent});
  135.  
  136. const checkboxLabel = document.createElement("label");
  137. checkboxLabel.appendChild(checkbox);
  138. checkboxLabel.appendChild(document.createTextNode(langOption.textContent));
  139.  
  140. const checkboxGroup = document.createElement("div");
  141. checkboxGroup.className = "checkbox";
  142. checkboxGroup.appendChild(checkboxLabel);
  143.  
  144. langs.appendChild(checkboxGroup);
  145. }
  146. },
  147.  
  148. updateButtons() {
  149. $(group).empty();//for (const child of group.children) group.removeChild(child);
  150.  
  151. for (const [languageId, {label}] of choices.entries()) {
  152. if (!config.isSet(languageId)) continue;
  153. const button = document.createElement("button");
  154. button.type = "button";
  155. button.className = "btn btn-default";
  156. button.dataset.languageId = languageId;
  157. button.textContent = label;
  158. button.addEventListener("click", _e => {
  159. //TODO: Set languageId
  160. $("#select-lang select[name='data.LanguageId']").val(languageId).trigger("change");
  161. });
  162. group.appendChild(button);
  163. }
  164.  
  165. this.highlightButtons();
  166. },
  167.  
  168. highlightButtons() {
  169. const select = $("#select-lang select[name='data.LanguageId']");
  170. for (const button of group.children) {
  171. button.classList.remove("btn-default", "btn-success");
  172. if (select.val() == button.dataset.languageId) {
  173. button.classList.add("btn-success");
  174. } else {
  175. button.classList.add("btn-default");
  176. }
  177. }
  178. },
  179.  
  180. showConfig() {
  181. for (const [languageId, {checkbox}] of choices.entries()) {
  182. checkbox.checked = config.isSet(languageId);
  183. }
  184. $(modal).modal();
  185. },
  186. };
  187. })();
  188.  
  189. // 初期化
  190. config.load();
  191. view.init();
  192.  
  193. console.info(`AtCoderLanguageButtons v${VERSION}`);
  194. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址