AtCoder Language Filter

提出言語のフィルタリングと並び替え

Verzia zo dňa 18.03.2020. Pozri najnovšiu verziu.

  1. // ==UserScript==
  2. // @name AtCoder Language Filter
  3. // @namespace https://github.com/morioprog
  4. // @version 1.0.0
  5. // @description 提出言語のフィルタリングと並び替え
  6. // @author morio_prog
  7. // @match *://atcoder.jp/contests/*/tasks/*
  8. // @match *://atcoder.jp/contests/*/submit*
  9. // @match *://atcoder.jp/contests/*/custom_test*
  10. // @license CC0
  11. // @require https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
  12. // @require https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/js/select2.min.js
  13. // ==/UserScript==
  14.  
  15. function moveElementToEndOfParent($ele) {
  16. const parent = $ele.parent();
  17. $ele.detach();
  18. parent.append($ele);
  19. }
  20.  
  21. (function() {
  22. 'use strict';
  23.  
  24. const deflang = localStorage.getItem('defaultLang').replace(/[^0-9]/g, '');
  25. const $select_language = $('#select-lang select');
  26.  
  27. /* Load saved languages */
  28. const lskey = 'userscript-languagefilter-savedlanguage';
  29. let savedlanguage = JSON.parse(localStorage.getItem(lskey));
  30. if (savedlanguage === null) {
  31. savedlanguage = ['C++14 (GCC 5.4.1)', 'Python3 (3.4.3)', 'PyPy3 (2.4.0)'];
  32. }
  33.  
  34. /* Build language-map */
  35. const optmap = new Map(); // {lang: [value, data-mime]}
  36. $select_language.children('option').each(function(i, e) {
  37. const $opt = $(e);
  38. optmap.set(
  39. $opt.text(),
  40. [
  41. $opt.attr('value'),
  42. $opt.attr('data-mime'),
  43. ]
  44. );
  45. $(this).remove();
  46. });
  47.  
  48. /* Add button */
  49. const buttonHtml = `<p><button type="button" class="btn btn-default btn-sm btn-auto-height" data-toggle="modal" data-target="#LangFilterModal">提出言語の選択</button></p>`;
  50. $('#main-container > div.row > div > form > div > div.editor-buttons').append(buttonHtml);
  51.  
  52. /* Add modal */
  53. const modalHtml = `
  54. <div class="modal fade" id="LangFilterModal" tabindex="-1" role="dialog" aria-labelledby="LangFilterModalLabel">
  55. <div class="modal-dialog" role="document">
  56. <div class="modal-content">
  57. <div class="modal-header">
  58. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  59. <h4 class="modal-title" id="LangFilterModalLabel">提出言語の選択</h4>
  60. </div>
  61. <div class="modal-body" id="langfilter-modal">
  62. <select id="langfilter-select2" multiple="multiple"></select>
  63. </div>
  64. <div class="modal-footer">
  65. <button type="button" id="langfilter-selectall" class="btn btn-info pull-left">Select All</button>
  66. <button type="button" id="langfilter-clear" class="btn btn-danger pull-left">Clear</button>
  67. <span id="langfilter-savelabel" style="margin-right:20px;"></span>
  68. <button type="button" id="langfilter-save" class="btn btn-success">Save changes</button>
  69. </div>
  70. </div>
  71. </div>
  72. </div>`;
  73. $('body').prepend(modalHtml);
  74.  
  75. const $lfsel2 = $('select#langfilter-select2');
  76. optmap.forEach(function(val, key) {
  77. $lfsel2.append(
  78. $('<option>', {
  79. value: val[0],
  80. text: key,
  81. })
  82. );
  83. });
  84.  
  85. let selected = [];
  86. $.each(savedlanguage, function(i, lang) {
  87. const val = optmap.get(lang)[0];
  88. const mim = optmap.get(lang)[1];
  89. $select_language.append(
  90. $('<option>', {
  91. 'value': val,
  92. 'data-mime': mim,
  93. 'text': lang,
  94. 'selected': val == deflang,
  95. })
  96. );
  97. selected.push(lang);
  98. const $opt = $lfsel2.find(`option[value='${val}']`);
  99. $opt.prop('selected', true);
  100. moveElementToEndOfParent($opt);
  101. });
  102.  
  103. /* Apply Select2 */
  104. $lfsel2.select2({
  105. placeholder: 'Languages',
  106. theme: 'bootstrap',
  107. }).on('select2:select', function(evt) {
  108. const $opt = $(this).children(`option[value=${evt.params.data.id}]`);
  109. moveElementToEndOfParent($opt);
  110. $(this).trigger("change");
  111. });
  112.  
  113. const $ul = $('#langfilter-modal > span > span.selection > span > ul.select2-selection__rendered');
  114. $ul.sortable({
  115. containment: 'parent',
  116. update: function() {
  117. $(this).children("li[title]").each(function(i, li) {
  118. const $opt = $lfsel2.children('option').filter(function() { return $(this).html() == li.title });
  119. moveElementToEndOfParent($opt);
  120. });
  121. }
  122. });
  123.  
  124. $('#langfilter-clear').on('click', function() {
  125. $lfsel2.val(null).trigger('change');
  126. });
  127.  
  128. $('#langfilter-selectall').on('click', function() {
  129. $lfsel2.children('option').prop('selected', true);
  130. $lfsel2.trigger("change");
  131. });
  132.  
  133. $('#langfilter-save').on('click', function() {
  134. selected = [];
  135. $ul.children('li.select2-selection__choice').each(function() {
  136. selected.push($(this).text().slice(1));
  137. });
  138. if (selected.length === 0) {
  139. $('#langfilter-savelabel').removeClass('text-success');
  140. $('#langfilter-savelabel').addClass('text-danger');
  141. $('#langfilter-savelabel').text('Please select at least 1 language!');
  142. } else {
  143. localStorage.setItem(lskey, JSON.stringify(selected));
  144. $('#langfilter-savelabel').removeClass('text-danger');
  145. $('#langfilter-savelabel').addClass('text-success');
  146. $('#langfilter-savelabel').text(`Saved (${(new Date()).toLocaleString()})`);
  147. }
  148. });
  149.  
  150. })();