Hide-Older-Comments

Hide old comments on Orbitar

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

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