atcoder-problem-navigator

Shows a navigation bar on AtCoder and Codeforces contest pages for jumping to problems.

  1. // ==UserScript==
  2. // @name atcoder-problem-navigator
  3. // @namespace https://github.com/yoshrc
  4. // @version 1.3
  5. // @description Shows a navigation bar on AtCoder and Codeforces contest pages for jumping to problems.
  6. // @author yoshrc
  7. // @include https://atcoder.jp/contests/*
  8. // @include https://codeforces.com/contest/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. const KEY_PREFIX = 'atcoder-problem-navigator-';
  16.  
  17. const do_atcoder = () => {
  18. const contest = location.href.match(/^https:\/\/atcoder\.jp\/contests\/([^\/?]+)/)[1];
  19. const key = KEY_PREFIX + 'atcoder-' + contest;
  20.  
  21. if (location.href.match(/^https:\/\/atcoder\.jp\/contests\/([^\/]+)\/tasks\/?$/)) {
  22. const problems = [];
  23. const rows = document.querySelectorAll('tbody>tr');
  24. for (let i = 0; i < rows.length; i++) {
  25. const links = rows[i].querySelectorAll('a');
  26. const href = links[0].getAttribute('href');
  27. const text = links[0].textContent + ' - ' + links[1].textContent;
  28. problems.push({
  29. href: href,
  30. text: text
  31. });
  32. }
  33. localStorage[key] = JSON.stringify(problems);
  34. }
  35.  
  36. if (key in localStorage) {
  37. let problems = JSON.parse(localStorage[key]);
  38. const problemsBar = document.createElement('ul');
  39. problemsBar.className = 'nav nav-tabs';
  40. for (let i = 0; i < problems.length; i++) {
  41. const link = document.createElement('a');
  42. link.setAttribute('style', 'margin-left: 10px; margin-right: 10px; white-space: nowrap');
  43. link.setAttribute('href', problems[i].href);
  44. link.textContent = problems[i].text;
  45. const span = document.createElement('span');
  46. span.textContent = ' ';
  47. span.appendChild(link);
  48. problemsBar.appendChild(span);
  49. }
  50. document.getElementById('contest-nav-tabs').appendChild(problemsBar);
  51. }
  52. };
  53.  
  54. const do_codeforces = () => {
  55. const contest = location.href.match(/^https:\/\/codeforces\.com\/contest\/([^\/?]+)/)[1];
  56. const key = KEY_PREFIX + 'codeforces-' + contest;
  57.  
  58. if (location.href.match(/^https:\/\/codeforces\.com\/contest\/([^\/]+)\/?$/)) {
  59. const problems = [];
  60. const rows = document.querySelectorAll('.problems>tbody>tr');
  61. // Starts from 1 to skip the header
  62. for (let i = 1; i < rows.length; i++) {
  63. const links = rows[i].querySelectorAll('a');
  64. const href = links[0].getAttribute('href');
  65. const text = links[0].textContent.trim() + '. ' + links[1].textContent;
  66. problems.push({
  67. href: href,
  68. text: text
  69. });
  70. }
  71. localStorage[key] = JSON.stringify(problems);
  72. }
  73.  
  74. if (key in localStorage) {
  75. let problems = JSON.parse(localStorage[key]);
  76. const problemsBar = document.createElement('ul');
  77. problemsBar.setAttribute('style', 'margin-left: 15px; margin-right: 280px; padding-top: 30px');
  78. for (let i = 0; i < problems.length; i++) {
  79. const link = document.createElement('a');
  80. link.setAttribute('style', 'margin-right: 20px; white-space: nowrap');
  81. link.setAttribute('href', problems[i].href);
  82. link.textContent = problems[i].text;
  83. const span = document.createElement('span');
  84. span.textContent = ' ';
  85. span.appendChild(link);
  86. problemsBar.appendChild(span);
  87. }
  88.  
  89. const content = document.getElementById('pageContent');
  90. content.parentNode.insertBefore(problemsBar, content);
  91. }
  92. };
  93.  
  94. if (location.href.match(/^https:\/\/atcoder\.jp\/contests\//)) {
  95. do_atcoder();
  96. } else if (location.href.match(/^https:\/\/codeforces\.com\/contest\//)) {
  97. do_codeforces();
  98. }
  99. })();

QingJ © 2025

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