Greasy Fork 支持简体中文。

4chan thread sorter

10/12/2024, 14:57:32

  1. // ==UserScript==
  2. // @name 4chan thread sorter
  3. // @namespace Violentmonkey Scripts
  4. // @match https://boards.4chan.org/*
  5. // @grant none
  6. // @version 1.0
  7. // @author Anon
  8. // @description 10/12/2024, 14:57:32
  9. // ==/UserScript==
  10.  
  11.  
  12. let currentOption = 'default';
  13. const thread = document.querySelector('.thread');
  14. if (!thread) return;
  15.  
  16.  
  17. // Post sorter
  18. const postOrderReplies = (post) => {
  19. const replyCount = post.querySelectorAll('.postInfo .backlink').length;
  20. post.style.order = 100 - replyCount;
  21. }
  22.  
  23. const postOrderCatbox = (post) => {
  24. const postContent = post.querySelector('.postMessage').textContent;
  25. const matches = postContent.match(/catbox\.moe/g);
  26. const catboxCount = matches ? matches.length : 0;
  27. post.style.order = 100 - catboxCount;
  28. }
  29.  
  30. const assignPostOrder = () => {
  31. if (currentOption === 'default') {
  32. thread.style.display = 'block';
  33. return;
  34. }
  35.  
  36. thread.style.display = 'flex';
  37. const posts = thread.querySelectorAll('.replyContainer');
  38.  
  39. if (currentOption === 'replies') {
  40. posts.forEach(post => postOrderReplies(post));
  41. } else if (currentOption === 'catbox') {
  42. posts.forEach(post => postOrderCatbox(post));
  43. }
  44. }
  45.  
  46.  
  47. // Create option select
  48. const selectOptions = ['Default', 'Replies', 'Catbox'];
  49. const selectElement = document.createElement('select');
  50.  
  51. selectOptions.forEach(option => {
  52. const optionElement = document.createElement('option');
  53. optionElement.value = option.toLowerCase();
  54. optionElement.textContent = option;
  55. selectElement.appendChild(optionElement);
  56. });
  57.  
  58. selectElement.id = 'thread-sort';
  59. document.body.appendChild(selectElement);
  60.  
  61. selectElement.addEventListener('change', (event) => {
  62. currentOption = event.target.value;
  63. assignPostOrder();
  64. });
  65.  
  66.  
  67. // Select observer
  68. const observerCallback = (mutationsList, observer) => {
  69. for (const mutation of mutationsList) {
  70. if (mutation.type === 'childList') {
  71. assignPostOrder();
  72. }
  73. }
  74. };
  75.  
  76. const threadObserver = new MutationObserver(observerCallback);
  77. threadObserver.observe(thread, { childList: true, subtree: false });
  78.  
  79.  
  80. // Styles
  81. const style = document.createElement('style');
  82. style.innerHTML = `
  83. .thread {
  84. flex-direction: column;
  85. }
  86.  
  87. .thread .opContainer {
  88. order: 1;
  89. }
  90.  
  91. #thread-sort {
  92. position: fixed;
  93. top: 2.5rem;
  94. right: 2rem;
  95. opacity: 0.5;
  96. padding: 0.4rem 0.6rem;
  97. background: white !important;
  98. border: none !important;
  99. border-radius: 0.2rem;
  100. transition: all ease 150ms;
  101. cursor: pointer;
  102. }
  103.  
  104. #thread-sort:hover {
  105. opacity: 1;
  106. }
  107. `;
  108.  
  109. document.head.appendChild(style);