Komica 檔案區自定義篩選與排序工具

這個腳本可以讓使用者自定義最小回覆數,按回覆數排序並且高亮顯示回覆數大於一定數值的貼文。

  1. // ==UserScript==
  2. // @name Komica 檔案區自定義篩選與排序工具
  3. // @namespace https://chat.openai.com/
  4. // @description 這個腳本可以讓使用者自定義最小回覆數,按回覆數排序並且高亮顯示回覆數大於一定數值的貼文。
  5. // @version 3.0.1
  6. // @author ChatGPT
  7. // @match https://*.komica1.org/*/pixmicat.php?mode=module&load=mod_threadlist*
  8. // @match https://*.komica2.cc/*/pixmicat.php?mode=module&load=mod_threadlist*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=komica.org
  10. // @grant none
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // 從 localStorage 中獲取最小回覆數和排序方式,如果沒有存儲則使用默認值
  18. let minRepliesToShow = parseInt(localStorage.getItem('minRepliesToShow')) || 30;
  19. let sortByReplies = localStorage.getItem('sortByReplies') === 'true';
  20. const table = document.querySelector('table[align="center"][width="97%"], #contents > table > tbody');
  21. const rows = Array.from(table.querySelectorAll('tr:not(:first-child)'));
  22. const responseIndex = Array.from(table.querySelectorAll('th')).findIndex(th => th.textContent.trim() === '回應');
  23. let rowsArray = [];
  24.  
  25. rows.forEach(row => {
  26. const replies = parseInt(row.cells[responseIndex]?.textContent.trim()) || 0;
  27. row.style.display = replies >= minRepliesToShow ? '' : 'none';
  28. });
  29.  
  30. const options = document.createElement('div');
  31. options.innerHTML = `
  32. <label for="min-replies-to-show">最小回應數</label>
  33. <select id="min-replies-to-show" name="min-replies-to-show">
  34. <option value="0" ${minRepliesToShow === 0 ? 'selected' : ''}>0</option>
  35. <option value="10" ${minRepliesToShow === 10 ? 'selected' : ''}>10</option>
  36. <option value="30" ${minRepliesToShow === 30 ? 'selected' : ''}>30</option>
  37. <option value="50" ${minRepliesToShow === 50 ? 'selected' : ''}>50</option>
  38. <option value="100" ${minRepliesToShow === 100 ? 'selected' : ''}>100</option>
  39. </select>
  40. <label for="sort-by-replies">按回應數排序</label>
  41. <input type="checkbox" id="sort-by-replies" name="sort-by-replies" checked>
  42. `;
  43. options.style.position = 'fixed';
  44. options.style.left = '20px';
  45. options.style.bottom = '20px';
  46. options.style.color = 'red';
  47. options.style.backgroundColor = 'white';
  48. options.style.padding = '20px';
  49. options.style.borderRadius = '20px';
  50. document.body.appendChild(options);
  51.  
  52. const minRepliesToShowInput = document.getElementById('min-replies-to-show');
  53. const sortByRepliesInput = document.getElementById('sort-by-replies');
  54. minRepliesToShowInput.addEventListener('change', (event) => {
  55. minRepliesToShow = parseInt(event.target.value);
  56. localStorage.setItem('minRepliesToShow', minRepliesToShow);
  57.  
  58. rows.forEach(row => {
  59. const replies = parseInt(row.cells[responseIndex]?.textContent.trim()) || 0;
  60. row.style.display = replies >= minRepliesToShow ? '' : 'none';
  61. });
  62. });
  63.  
  64. sortByRepliesInput.addEventListener('change', (event) => {
  65. sortByReplies = event.target.checked;
  66. localStorage.setItem('sortByReplies', sortByReplies);
  67. if (sortByReplies) {
  68. sortRows();
  69. } else {
  70. restoreRowOrder();
  71. }
  72. });
  73.  
  74. function restoreRowOrder() {
  75. rows.forEach(row => {
  76. table.appendChild(row);
  77. });
  78. }
  79. function sortRows() {
  80. rowsArray = Array.from(rows);
  81. rowsArray.sort(function(row1, row2) {
  82. var response1 = parseInt(row1.cells[responseIndex].textContent.trim(), 10);
  83. var response2 = parseInt(row2.cells[responseIndex].textContent.trim(), 10);
  84. return response2 - response1;
  85. });
  86.  
  87. for (var j = 0; j < rowsArray.length; j++) {
  88. table.appendChild(rowsArray[j]);
  89. }
  90. }
  91.  
  92. document.getElementById('sort-by-replies').addEventListener('change', (event) => {
  93. sortByReplies = event.target.checked;
  94. if (sortByReplies) {
  95. sortRows();
  96. } else {
  97. restoreRowOrder();
  98. }
  99. });
  100.  
  101. sortRows();
  102.  
  103. minRepliesToShowInput.dispatchEvent(new Event('change'));
  104.  
  105. var style = document.createElement('style');
  106. style.innerHTML = '.highlight-yellow { background-color: yellow; } .highlight-green { background-color: #D8FAD8;; }';
  107. document.head.appendChild(style);
  108.  
  109. rowsArray.forEach(row => {
  110. let replies = parseInt(row.cells[responseIndex].textContent.trim()) || 0;
  111. if (replies >= 100) {
  112. row.classList.remove('ListRow1_bg', 'ListRow2_bg');
  113. row.classList.add('highlight-yellow');
  114. row.style.fontWeight = 'bold';
  115. } else if (replies >= 50) {
  116. row.classList.remove('ListRow1_bg', 'ListRow2_bg');
  117. row.classList.add('highlight-green');
  118. row.style.fontWeight = 'bold';
  119. }
  120. });
  121.  
  122. window.addEventListener('load', () => {
  123. if (localStorage.getItem('sortByReplies') === 'false') {
  124. sortByRepliesInput.checked = false;
  125. restoreRowOrder();
  126. }
  127.  
  128. const replyLinks = document.querySelectorAll('a[href^="pixmicat.php?res="]');
  129. replyLinks.forEach(link => {
  130. link.target = "_blank";
  131. });
  132. });
  133. })();