Hide-Older-Comments

Hide old comments, highlight only parent text if it has newer children

当前为 2025-02-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Hide-Older-Comments
  3. // @namespace https://orbitar.space/
  4. // @version 1.8.0
  5. // @description Hide old comments, highlight only parent text if it has newer children
  6. // @match https://*.orbitar.space/*
  7. // @match https://*.orbitar.local/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. console.log("✅ Comment filtering script is running...");
  16.  
  17. let observer; // Global observer to allow disconnecting on URL change
  18.  
  19. function isPostPage() {
  20. return /^https:\/\/([^\.]*\.)?orbitar\.space\/(p\d+|s\/[^\/]+\/p\d+)([\?].*|[\#].*)?$/.test(window.location.href);
  21. }
  22.  
  23. function observeForLinksSection() {
  24. if (observer) {
  25. observer.disconnect(); // Disconnect previous observer if it exists
  26. }
  27. observer = new MutationObserver(() => {
  28. let linksSection = document.querySelector('[class^="PostPage_postButtons__"]');
  29. if (linksSection && !document.getElementById("comment-filter-container")) {
  30. createUI(linksSection);
  31. }
  32. });
  33. observer.observe(document.body, { childList: true, subtree: true });
  34. }
  35.  
  36. function createUI(linksSection) {
  37. if (document.getElementById("comment-filter-container")) return;
  38.  
  39. let container = document.createElement("span");
  40. container.id = "comment-filter-container";
  41. //container.style.marginLeft = "10px";
  42.  
  43. let divider = document.createTextNode(" • ");
  44. container.appendChild(divider);
  45.  
  46. let showFilterLink = document.createElement("a");
  47. showFilterLink.href = "#fr";
  48. showFilterLink.innerText = "фильтровать по дате";
  49. showFilterLink.onclick = (event) => {
  50. event.preventDefault();
  51. filterUI.style.display = "inline";
  52. showFilterLink.style.display = "none";
  53. };
  54.  
  55. let filterUI = document.createElement("span");
  56. filterUI.style.display = "none";
  57. filterUI.style.marginLeft = "10px";
  58.  
  59. let dateInput = document.createElement("input");
  60. dateInput.type = "datetime-local";
  61. dateInput.id = "comment-filter-date";
  62. dateInput.style.marginRight = "5px";
  63.  
  64. let filterLink = document.createElement("a");
  65. filterLink.href = "#fr";
  66. filterLink.innerText = "фильтровать";
  67. filterLink.onclick = (event) => {
  68. event.preventDefault();
  69. filterComments();
  70. };
  71.  
  72. let clearLink = document.createElement("a");
  73. clearLink.href = "#fr";
  74. clearLink.innerText = "очистить";
  75. clearLink.style.marginLeft = "10px";
  76. clearLink.onclick = (event) => {
  77. event.preventDefault();
  78. clearFilter();
  79. };
  80.  
  81. let quickLinks = document.createElement("div");
  82. quickLinks.style.marginTop = "10px";
  83.  
  84. ["3 часа", "12 часов", "сутки", "3 дня", "неделя"].forEach((text, index) => {
  85. let timeAdjustLink = document.createElement("a");
  86. timeAdjustLink.href = "#ft";
  87. timeAdjustLink.innerText = text;
  88. timeAdjustLink.style.marginRight = "5px";
  89. timeAdjustLink.onclick = (event) => {
  90. event.preventDefault();
  91. adjustDate(index);
  92. };
  93. quickLinks.appendChild(timeAdjustLink);
  94. if (index < 4) quickLinks.appendChild(document.createTextNode(" | "));
  95. });
  96. ["gray", "pink", "blue", "skyblue", "salmon" ].forEach((color, index) => {
  97. quickLinks.appendChild(document.createTextNode(" | ")); // Add divider
  98. let colorLink = document.createElement("a");
  99. colorLink.href = "#fc";
  100. colorLink.innerText = color;
  101. colorLink.style.marginRight = "5px";
  102. colorLink.onclick = (event) => {
  103. event.preventDefault();
  104. changeParentCommentBgColor("light" + color);
  105. };
  106. quickLinks.appendChild(colorLink);
  107. });
  108.  
  109. filterUI.appendChild(dateInput);
  110. filterUI.appendChild(filterLink);
  111. filterUI.appendChild(clearLink);
  112. filterUI.appendChild(quickLinks);
  113.  
  114. container.appendChild(showFilterLink);
  115. container.appendChild(filterUI);
  116. linksSection.appendChild(container);
  117. }
  118.  
  119. let parentCommentBgColor = 'lightpink'; // Default background color
  120.  
  121. function changeParentCommentBgColor(color) {
  122. parentCommentBgColor = color;
  123. let comments = document.querySelectorAll('.comment');
  124. comments.forEach(comment => {
  125. let commentTextContainer = comment.querySelector('[class^="CommentComponent_content__"]');
  126. if (commentTextContainer &&
  127. (commentTextContainer.style.backgroundColor === 'lightgray' ||
  128. commentTextContainer.style.backgroundColor === 'lightpink' ||
  129. commentTextContainer.style.backgroundColor === 'lightblue' ||
  130. commentTextContainer.style.backgroundColor === 'lightskyblue' ||
  131. commentTextContainer.style.backgroundColor === 'lightsalmon')) {
  132. commentTextContainer.style.backgroundColor = color;
  133. }
  134. });
  135. }
  136.  
  137. function adjustDate(option) {
  138. let dateInput = document.getElementById("comment-filter-date");
  139. if (!dateInput) return;
  140.  
  141. let currentDate = new Date();
  142. switch (option) {
  143. case 0:
  144. currentDate.setHours(currentDate.getHours() - 3);
  145. break;
  146. case 1:
  147. currentDate.setHours(currentDate.getHours() - 12);
  148. break;
  149. case 2:
  150. currentDate.setDate(currentDate.getDate() - 1);
  151. break;
  152. case 3:
  153. currentDate.setDate(currentDate.getDate() - 3);
  154. break;
  155. case 4:
  156. currentDate.setDate(currentDate.getDate() - 7);
  157. break;
  158. }
  159.  
  160. let localISOTime = new Date(currentDate.getTime() - currentDate.getTimezoneOffset() * 60000)
  161. .toISOString()
  162. .slice(0, 16);
  163. dateInput.value = localISOTime;
  164. }
  165.  
  166. function filterComments() {
  167. let dateInput = document.getElementById("comment-filter-date");
  168. if (!dateInput) return;
  169. let selectedDate = new Date(dateInput.value);
  170. if (isNaN(selectedDate)) {
  171. console.error("Invalid date selected.");
  172. return;
  173. }
  174.  
  175. console.log("Filtering comments older than:", selectedDate.toString());
  176.  
  177. let comments = document.querySelectorAll('.comment');
  178. let commentMap = new Map();
  179.  
  180. comments.forEach(comment => {
  181. let dateElement = findDateElement(comment);
  182. if (!dateElement) return;
  183.  
  184. let commentDateText = dateElement.innerText.trim();
  185. let commentDate = parseCommentDate(commentDateText);
  186. let commentId = comment.dataset.commentId;
  187. if (isNaN(commentDate)) return;
  188.  
  189. commentMap.set(commentId, { comment, commentDate, hasNewerChild: false });
  190. });
  191.  
  192. comments.forEach(comment => {
  193. let commentId = comment.dataset.commentId;
  194. let commentData = commentMap.get(commentId);
  195. if (!commentData) return;
  196.  
  197. let { commentDate } = commentData;
  198. let childComments = [...comment.querySelectorAll('.comment')];
  199. let hasNewerChild = childComments.some(child => {
  200. let childId = child.dataset.commentId;
  201. return commentMap.has(childId) && commentMap.get(childId).commentDate >= selectedDate;
  202. });
  203. if (hasNewerChild) {
  204. commentData.hasNewerChild = true;
  205. }
  206. });
  207.  
  208. comments.forEach(comment => {
  209. let commentId = comment.dataset.commentId;
  210. let commentData = commentMap.get(commentId);
  211. if (!commentData) return;
  212.  
  213. let { commentDate, hasNewerChild } = commentData;
  214. let commentTextContainer = comment.querySelector('[class^="CommentComponent_content__"]');
  215. if (!commentTextContainer) return;
  216.  
  217. if (commentDate >= selectedDate) {
  218. comment.style.display = "";
  219. commentTextContainer.style.backgroundColor = "";
  220. } else if (hasNewerChild) {
  221. comment.style.display = "";
  222. commentTextContainer.style.padding = "5px";
  223. commentTextContainer.style.backgroundColor = parentCommentBgColor;
  224. } else {
  225. comment.style.display = "none";
  226. }
  227. });
  228. }
  229.  
  230. function clearFilter() {
  231. let comments = document.querySelectorAll('.comment');
  232. comments.forEach(comment => {
  233. comment.style.display = "";
  234. let commentTextContainer = comment.querySelector('[class^="CommentComponent_content__"]');
  235. if (commentTextContainer) {
  236. commentTextContainer.style.backgroundColor = "";
  237. }
  238. });
  239. console.log("Filter cleared, all comments visible.");
  240. }
  241.  
  242. function findDateElement(comment) {
  243. let signature = comment.querySelector('[class^="SignatureComponent_signature__"]');
  244. if (!signature) return null;
  245.  
  246. let dateLinks = signature.querySelectorAll("a");
  247. return dateLinks.length >= 2 ? dateLinks[1] : null;
  248. }
  249.  
  250. function parseCommentDate(dateText) {
  251. let months = {
  252. "января": "January", "февраля": "February", "марта": "March",
  253. "апреля": "April", "мая": "May", "июня": "June",
  254. "июля": "July", "августа": "August", "сентября": "September",
  255. "октября": "October", "ноября": "November", "декабря": "December"
  256. };
  257.  
  258. // Handle "вчера в 12:34" format
  259. if (dateText.startsWith("вчера в")) {
  260. let match = dateText.match(/вчера в (\d{1,2}):(\d{2})/);
  261. if (match) {
  262. let yesterday = new Date();
  263. yesterday.setDate(yesterday.getDate() - 1);
  264. yesterday.setHours(parseInt(match[1], 10), parseInt(match[2], 10), 0, 0);
  265. return yesterday;
  266. }
  267. }
  268.  
  269. // Handle "сегодня в 12:34" format
  270. if (dateText.startsWith("сегодня в")) {
  271. let match = dateText.match(/сегодня в (\d{1,2}):(\d{2})/);
  272. if (match) {
  273. let today = new Date();
  274. today.setHours(parseInt(match[1], 10), parseInt(match[2], 10), 0, 0);
  275. return today;
  276. }
  277. }
  278.  
  279. // Handle Old Format: "9 февраля в 12:12"
  280. let oldFormatMatch = dateText.match(/(\d{1,2})\s([а-яА-Я]+)\sв\s(\d{1,2}):(\d{2})/);
  281. if (oldFormatMatch) {
  282. let [_, day, monthName, hour, minute] = oldFormatMatch;
  283. let month = months[monthName];
  284. if (!month) return NaN;
  285. let currentYear = new Date().getFullYear();
  286. return new Date(`${day} ${month} ${currentYear} ${hour}:${minute}`);
  287. }
  288.  
  289. // Handle New Format: "27.08.2024 12:17"
  290. let newFormatMatch = dateText.match(/(\d{2})\.(\d{2})\.(\d{4})\s(\d{2}):(\d{2})/);
  291. if (newFormatMatch) {
  292. let [_, day, month, year, hour, minute] = newFormatMatch;
  293. return new Date(`${year}-${month}-${day}T${hour}:${minute}:00`);
  294. }
  295.  
  296. return NaN; // If no format matched, return NaN
  297. }
  298.  
  299. function runScript() {
  300. if (isPostPage()) {
  301. // Remove existing UI container if present to avoid duplicates
  302. let existingContainer = document.getElementById("comment-filter-container");
  303. if (existingContainer) {
  304. existingContainer.remove();
  305. }
  306. // Clear any applied filters before reinitializing
  307. clearFilter();
  308. observeForLinksSection();
  309. }
  310. }
  311.  
  312. // Run the script initially if on a post page
  313. runScript();
  314.  
  315. // Listen for URL changes via popstate (back/forward navigation)
  316. window.addEventListener('popstate', runScript);
  317.  
  318. // Listen for hash changes if navigation uses URL fragments
  319. window.addEventListener('hashchange', runScript);
  320.  
  321. // Override pushState to detect URL changes
  322. const originalPushState = history.pushState;
  323. history.pushState = function () {
  324. originalPushState.apply(this, arguments);
  325. runScript();
  326. };
  327.  
  328. // Override replaceState similarly
  329. const originalReplaceState = history.replaceState;
  330. history.replaceState = function () {
  331. originalReplaceState.apply(this, arguments);
  332. runScript();
  333. };
  334. })();