AtCoderLanguageButtons

Add buttons to select language.

Ekde 2021/02/13. Vidu La ĝisdata versio.

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