Greasy Fork is available in English.

AtCoder Listing Tasks

Click [Tasks] tab to open a drop-down list linked to each task.

As of 28.05.2023. See ბოლო ვერსია.

  1. // ==UserScript==
  2. // @name AtCoder Listing Tasks
  3. // @namespace https://github.com/luuguas/AtCoderListingTasks
  4. // @version 1.0
  5. // @description Click [Tasks] tab to open a drop-down list linked to each task.
  6. // @description [問題]タブをクリックすると、各問題へのリンクをドロップダウンリストで表示します。
  7. // @author luuguas
  8. // @license Apache
  9. // @match https://atcoder.jp/contests/*
  10. // @exclude https://atcoder.jp/contests/
  11. // @exclude https://atcoder.jp/contests/archive
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17. const contest_url = 'https://atcoder.jp/contests/';
  18. const unique_tag = 'user-script-listing-tasks';
  19.  
  20. //[問題]タブに本UserScript用のidを追加(タブがなければfalseを返す)
  21. function AttachId(){
  22. let tabs = document.getElementById('contest-nav-tabs');
  23. if(tabs === null){
  24. return false;
  25. }
  26. let tasks_tab = tabs.querySelector('a[href$="tasks"]');
  27. if(tasks_tab === null){
  28. return false;
  29. }
  30. else{
  31. tasks_tab.setAttribute('id', unique_tag);
  32. return true;
  33. }
  34. }
  35.  
  36. //[提出結果]タブと同様のドロップダウンリストに変える(中身は空)
  37. function Togglize(){
  38. let tasks_tab = document.getElementById(unique_tag);
  39.  
  40. let attr = {
  41. 'class': 'dropdown-toggle',
  42. 'data-toggle': 'dropdown',
  43. 'href': '#',
  44. 'role:': 'button',
  45. 'aria-haspopup': 'true',
  46. 'aria-expanded': 'false',
  47. };
  48. for(let [key, value] of Object.entries(attr)){
  49. tasks_tab.setAttribute(key, value);
  50. }
  51.  
  52. let caret = document.createElement('span');
  53. caret.className = 'caret';
  54. tasks_tab.appendChild(caret);
  55.  
  56. let dropdown_menu = document.createElement('ul');
  57. dropdown_menu.className = 'dropdown-menu';
  58. tasks_tab.parentNode.appendChild(dropdown_menu);
  59. }
  60.  
  61. //リストを追加する
  62. function AddList(){
  63. let url = location.href;
  64. let contest_name = url.split('/')[4];
  65.  
  66. let tasks_tab_li = document.getElementById(unique_tag).parentNode;
  67. let dropdown_menu = tasks_tab_li.querySelector('.dropdown-menu');
  68.  
  69. //[問題一覧]の追加
  70. let all_tasks = document.createElement('li');
  71. let lang = document.querySelector('meta[http-equiv="Content-Language"]');
  72. if(lang !== null && lang.getAttribute('content') === 'en'){
  73. all_tasks.innerHTML = '<a href="' + contest_url + contest_name + '/tasks"><span class="glyphicon glyphicon-list" aria-hidden="true"></span> All Tasks</a>';
  74. }
  75. else{
  76. all_tasks.innerHTML = '<a href="' + contest_url + contest_name + '/tasks"><span class="glyphicon glyphicon-list" aria-hidden="true"></span> 問題一覧</a>';
  77. }
  78. dropdown_menu.appendChild(all_tasks);
  79.  
  80. //分割線の追加
  81. let divider = document.createElement('li');
  82. divider.className = 'divider';
  83. dropdown_menu.appendChild(divider);
  84.  
  85. //https://atcoder.jp/contests/***/tasks から問題のリストを抽出
  86. let xhr = new XMLHttpRequest();
  87. xhr.responseType = 'document';
  88. xhr.onreadystatechange = function(){
  89. let tasks_tab_li = document.getElementById(unique_tag).parentNode;
  90. let dropdown_menu = tasks_tab_li.querySelector('.dropdown-menu');
  91.  
  92. if(xhr.readyState === 4){
  93. if(xhr.status === 200){
  94. let result = xhr.responseXML;
  95. let problem_node = result.querySelector('#contest-nav-tabs + .col-sm-12');
  96. let problem_list = problem_node.querySelectorAll('table.table tbody tr');
  97.  
  98. //問題情報を抽出
  99. let list = [];
  100. for(let li of problem_list){
  101. let td = li.querySelectorAll('td');
  102. let problem_url = td[0].firstChild.getAttribute('href');
  103. let problem_diff = td[0].firstChild.textContent;
  104. let problem_name = td[1].firstChild.textContent;
  105. list.push({
  106. url: problem_url,
  107. diff: problem_diff,
  108. name: problem_name,
  109. });
  110. }
  111.  
  112. //リストを追加
  113. for(let data of list){
  114. let li = document.createElement('li');
  115. let a = document.createElement('a');
  116. a.setAttribute('href', data.url);
  117. a.textContent = data.diff + ' - ' + data.name;
  118. li.appendChild(a);
  119. dropdown_menu.appendChild(li);
  120. }
  121. console.log('[AtCoder Listing Tasks] Succeeded!');
  122. }
  123. else{
  124. let li = document.createElement('li');
  125. let a = document.createElement('a');
  126. a.setAttribute('href', 'javascript:void(0)');
  127. a.textContent = '(読み込み失敗)';
  128. li.appendChild(a);
  129. dropdown_menu.appendChild(li);
  130. console.log('[AtCoder Listing Tasks] Failed...');
  131. }
  132. }
  133. };
  134. xhr.open('GET', 'https://atcoder.jp/contests/' + contest_name + '/tasks', true);
  135. xhr.send(null);
  136. }
  137.  
  138. let tab_exist = AttachId();
  139. if(!tab_exist){
  140. //タブがない場合は終了
  141. console.log('[AtCoder Listing Tasks] [Tasks] Tab isn\'t exist.');
  142. return;
  143. }
  144. Togglize();
  145. AddList();
  146.  
  147. })();