TODO Problem Manager

Save TODO Problems for CF, QOJ, and AtCoder

当前为 2024-12-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name TODO Problem Manager
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description Save TODO Problems for CF, QOJ, and AtCoder
  6. // @author jakao
  7. // @license MIT
  8. // @match *://qoj.ac/*
  9. // @match *://codeforces.com/contest/*
  10. // @match *://codeforces.com/gym/*
  11. // @match *://atcoder.jp/*
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. function getUrls() {
  20. return GM_getValue('savedUrls', {});
  21. }
  22.  
  23. function saveUrls(urls) {
  24. GM_setValue('savedUrls', urls);
  25. }
  26.  
  27. const modal = document.createElement('div');
  28.  
  29. function createModal() {
  30. modal.id = 'urlModal';
  31. modal.style.position = 'fixed';
  32. modal.style.top = '50%';
  33. modal.style.left = '50%';
  34. modal.style.transform = 'translate(-50%, -50%)';
  35. modal.style.zIndex = '10000';
  36. modal.style.backgroundColor = '#fff';
  37. modal.style.border = '1px solid #ccc';
  38. modal.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
  39. modal.style.padding = '20px';
  40. modal.style.width = '80%';
  41. modal.style.maxWidth = '600px';
  42. modal.style.maxHeight = '80%';
  43. modal.style.overflowY = 'auto';
  44. modal.style.display = 'none';
  45. document.body.appendChild(modal);
  46. }
  47.  
  48. function displaySavedUrls() {
  49. const urls = getUrls();
  50. createModal();
  51. // 清空彈窗內容,保留 Close 按鈕
  52. modal.innerHTML = '<button style="position: absolute; top: 10px; right: 10px; background-color: #f50057; color: #fff; border: none; padding: 5px; cursor: pointer;" onclick="document.getElementById(\'urlModal\').style.display=\'none\';">Close</button>';
  53. modal.innerHTML += '<h2>Saved Problems</h2>';
  54.  
  55. for (const domain in urls) {
  56. const domainSection = document.createElement('div');
  57. domainSection.innerHTML = `<h3>${domain}</h3>`;
  58. const urlList = document.createElement('ul');
  59.  
  60. urls[domain].forEach((url, index) => {
  61. const listItem = document.createElement('li');
  62. listItem.innerHTML = `
  63. <button style="margin-left: 10px;">Delete</button>
  64. <a href="${url.link}" target="_blank">${url.name}</a>
  65. `;
  66.  
  67. const deleteButton = listItem.querySelector('button');
  68. deleteButton.onclick = () => {
  69. deleteUrl(domain, index);
  70. };
  71.  
  72. urlList.appendChild(listItem);
  73. });
  74.  
  75. domainSection.appendChild(urlList);
  76. modal.appendChild(domainSection);
  77. }
  78.  
  79.  
  80. const saveButton = document.createElement('button');
  81. saveButton.textContent = 'Save Problem';
  82. saveButton.style.position = 'absolute';
  83. saveButton.style.top = '10px';
  84. saveButton.style.right = '70px';
  85. saveButton.style.backgroundColor = '#3D9140';
  86. saveButton.style.color = '#fff';
  87. saveButton.style.border = 'none';
  88. saveButton.style.padding = '5px';
  89. saveButton.style.cursor = 'pointer';
  90.  
  91. saveButton.onclick = function() {
  92. addUrl();
  93. };
  94. modal.appendChild(saveButton);
  95.  
  96. modal.style.display = 'block';
  97. }
  98.  
  99. function deleteUrl(domain, index) {
  100. const urls = getUrls();
  101. if (urls[domain]) {
  102. urls[domain].splice(index, 1);
  103. if (urls[domain].length === 0) {
  104. delete urls[domain];
  105. }
  106. saveUrls(urls);
  107. displaySavedUrls();
  108. alert('URL deleted successfully!');
  109. }
  110. }
  111.  
  112. function addUrl(){
  113. const currentProb = {
  114. "link": window.location.href,
  115. "name": window.location.href
  116. };
  117.  
  118. const regexCF = /^https:\/\/codeforces\.com\/(gym|contest)\/\d+\/problem\/[A-Za-z0-9]+$/;
  119. const regexQOJ = /^https:\/\/qoj\.ac\/contest\/\d+\/problem\/[A-Za-z0-9]+$/;
  120. const regexAC = /^https:\/\/atcoder\.jp\/contests\/[^\/]+\/tasks\/[^\/]+$/;
  121.  
  122. if (regexCF.test(currentProb.link)) {
  123. const parts = currentProb.link.split('/');
  124. currentProb.name = parts[4] + parts[6];
  125. }
  126. else if (regexQOJ.test(currentProb.link)) {
  127. const parts = currentProb.link.split('/');
  128. currentProb.name = parts[6];
  129. }
  130. else if (regexAC.test(currentProb.link)) {
  131. const parts = currentProb.link.split('/');
  132. currentProb.name = parts[6];
  133. }
  134.  
  135. const domain = new URL(currentProb.link).hostname.split('.')[0];
  136. const urls = getUrls();
  137.  
  138. if (!urls[domain]) {
  139. urls[domain] = [];
  140. }
  141.  
  142. if (!urls[domain].some(item => item.link === currentProb.link)) {
  143. urls[domain].push(currentProb);
  144. saveUrls(urls);
  145. displaySavedUrls();
  146. alert('URL saved successfully!');
  147. } else {
  148. alert('URL is already saved.');
  149. }
  150. }
  151.  
  152. function addViewButton() {
  153. const viewButton = document.createElement('button');
  154. viewButton.textContent = 'View Saved Problems';
  155. viewButton.style.position = 'fixed';
  156. viewButton.style.bottom = '10px';
  157. viewButton.style.right = '10px';
  158. viewButton.style.zIndex = '9999';
  159. viewButton.style.backgroundColor = '#3f51b5';
  160. viewButton.style.color = '#fff';
  161. viewButton.style.border = 'none';
  162. viewButton.style.padding = '10px';
  163. viewButton.style.cursor = 'pointer';
  164.  
  165. viewButton.onclick = displaySavedUrls;
  166.  
  167. document.body.appendChild(viewButton);
  168. }
  169.  
  170. function init() {
  171. createModal();
  172.  
  173. addViewButton();
  174. window.deleteUrl = deleteUrl;
  175. window.addUrl = addUrl;
  176. }
  177.  
  178. init();
  179. })();