// ==UserScript==
// @name Hide-Older-Comments
// @namespace https://orbitar.space/
// @version 1.8.8
// @description Hide old comments on Orbitar. donations: https://orbitar.space/u/pazoozoo
// @match https://*.orbitar.space/*
// @match https://*.orbitar.local/*
// @grant GM.getValue
// @grant GM.setValue
// @grant GM_getValue
// @grant GM_setValue
// @author pazoozoo
// @homepageURL https://orbitar.space/u/pazoozoo
// @license MIT
// ==/UserScript==
(function () {
'use strict';
console.log("✅ Comment filtering script is running...");
let observer; // Global observer to allow disconnecting on URL change
const SELECTORS = {
linksSection: '[class^="PostPage_postButtons__"]',
comment: '.comment',
commentContent: '[class^="CommentComponent_content__"]',
signature: '[class^="SignatureComponent_signature__"]'
};
const COLORS = ['gray', 'pink', 'blue', 'skyblue', 'salmon'];
const TIME_ADJUSTMENTS = [
{ text: "3 часа", adjust: (date) => date.setHours(date.getHours() - 3) },
{ text: "12 часов", adjust: (date) => date.setHours(date.getHours() - 12) },
{ text: "сутки", adjust: (date) => date.setDate(date.getDate() - 1) },
{ text: "3 дня", adjust: (date) => date.setDate(date.getDate() - 3) },
{ text: "неделя", adjust: (date) => date.setDate(date.getDate() - 7) }
];
// Default color that will be used if no saved preference exists
const DEFAULT_BG_COLOR = 'lightskyblue';
// Will store the actual background color (loaded from GM or default)
let parentCommentBgColor;
// Function to load the saved color preference
async function loadSavedColor() {
try {
const savedColor = await GM.getValue('parentCommentBgColor', DEFAULT_BG_COLOR);
return savedColor || DEFAULT_BG_COLOR; // Fallback if saved color is null/undefined
} catch (error) {
console.error("Error loading saved color:", error);
return DEFAULT_BG_COLOR;
}
}
// Function to save the color preference
async function saveColor(color) {
try {
await GM.setValue('parentCommentBgColor', color);
console.log("Color preference saved:", color);
} catch (error) {
console.error("Error saving color preference:", error);
}
}
// Initialize the color at startup
(async function initializeColor() {
parentCommentBgColor = await loadSavedColor();
console.log("Using background color:", parentCommentBgColor);
})();
function isPostPage() {
return /^https:\/\/([^\.]*\.)?orbitar\.space\/(p\d+|s\/[^\/]+\/p\d+)([\?].*|[\#].*)?$/.test(window.location.href);
}
function observeForLinksSection() {
if (observer) {
observer.disconnect(); // Disconnect previous observer if it exists
}
observer = new MutationObserver(() => {
let linksSection = document.querySelector(SELECTORS.linksSection);
if (linksSection && !document.getElementById("comment-filter-container")) {
createUI(linksSection);
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
function createUI(linksSection) {
if (document.getElementById("comment-filter-container")) return;
let container = document.createElement("span");
container.id = "comment-filter-container";
container.appendChild(document.createTextNode(" • "));
let showFilterLink = createLink("фильтровать по дате", "#fr", (event) => {
event.preventDefault();
filterUI.style.display = "inline";
showFilterLink.style.display = "none";
});
let filterUI = document.createElement("span");
filterUI.style.display = "none";
filterUI.style.marginLeft = "10px";
let dateInput = createDateInput("comment-filter-date");
let filterLink = createLink("фильтровать", "#ff", (event) => {
event.preventDefault();
filterComments();
});
let clearLink = createLink("очистить", "#fr", (event) => {
event.preventDefault();
clearFilter();
});
let quickLinks = document.createElement("div");
quickLinks.style.marginTop = "10px";
TIME_ADJUSTMENTS.forEach(({ text, adjust }, index) => {
let timeAdjustLink = createLink(text, "#ft", (event) => {
event.preventDefault();
adjustDate(adjust);
});
quickLinks.appendChild(timeAdjustLink);
if (index < TIME_ADJUSTMENTS.length - 1) quickLinks.appendChild(document.createTextNode(" | "));
});
COLORS.forEach((color, index) => {
quickLinks.appendChild(document.createTextNode(" | "));
let colorLink = createLink(color, "#fc", (event) => {
event.preventDefault();
changeParentCommentBgColor("light" + color);
});
quickLinks.appendChild(colorLink);
});
filterUI.appendChild(dateInput);
filterUI.appendChild(filterLink);
filterUI.appendChild(clearLink);
filterUI.appendChild(quickLinks);
container.appendChild(showFilterLink);
container.appendChild(filterUI);
linksSection.appendChild(container);
}
function createLink(text, href, onClick) {
let link = document.createElement("a");
link.href = href;
link.innerText = text;
link.style.marginRight = "5px";
link.onclick = onClick;
return link;
}
function createDateInput(id) {
let input = document.createElement("input");
input.type = "datetime-local";
input.id = id;
input.style.marginRight = "5px";
return input;
}
function changeParentCommentBgColor(color) {
parentCommentBgColor = color;
// Save the color preference
saveColor(color);
let comments = document.querySelectorAll(SELECTORS.comment);
comments.forEach(comment => {
let commentTextContainer = comment.querySelector(SELECTORS.commentContent);
if (commentTextContainer && COLORS.map(c => "light" + c).includes(commentTextContainer.style.backgroundColor)) {
commentTextContainer.style.backgroundColor = color;
}
});
}
function adjustDate(adjust) {
let dateInput = document.getElementById("comment-filter-date");
if (!dateInput) return;
let currentDate = new Date();
adjust(currentDate);
let localISOTime = new Date(currentDate.getTime() - currentDate.getTimezoneOffset() * 60000)
.toISOString()
.slice(0, 16);
dateInput.value = localISOTime;
}
function filterComments() {
let dateInput = document.getElementById("comment-filter-date");
if (!dateInput) return;
let selectedDate = new Date(dateInput.value);
if (isNaN(selectedDate)) {
console.error("Invalid date selected.");
return;
}
console.log("Filtering comments older than:", selectedDate.toString());
let comments = document.querySelectorAll(SELECTORS.comment);
let commentMap = new Map();
comments.forEach(comment => {
let dateElement = findDateElement(comment);
if (!dateElement) return;
let commentDateText = dateElement.innerText.trim();
let commentDate = parseCommentDate(commentDateText);
let commentId = comment.dataset.commentId;
if (isNaN(commentDate)) return;
commentMap.set(commentId, { comment, commentDate, hasNewerChild: false });
});
comments.forEach(comment => {
let commentId = comment.dataset.commentId;
let commentData = commentMap.get(commentId);
if (!commentData) return;
let { commentDate } = commentData;
let childComments = [...comment.querySelectorAll(SELECTORS.comment)];
let hasNewerChild = childComments.some(child => {
let childId = child.dataset.commentId;
return commentMap.has(childId) && commentMap.get(childId).commentDate >= selectedDate;
});
if (hasNewerChild) {
commentData.hasNewerChild = true;
}
});
comments.forEach(comment => {
let commentId = comment.dataset.commentId;
let commentData = commentMap.get(commentId);
if (!commentData) return;
let { commentDate, hasNewerChild } = commentData;
let commentTextContainer = comment.querySelector(SELECTORS.commentContent);
if (!commentTextContainer) return;
if (commentDate >= selectedDate) {
comment.style.display = "";
commentTextContainer.style.backgroundColor = "";
} else if (hasNewerChild) {
comment.style.display = "";
commentTextContainer.style.padding = "5px";
commentTextContainer.style.backgroundColor = parentCommentBgColor;
} else {
comment.style.display = "none";
}
});
}
function clearFilter() {
let comments = document.querySelectorAll(SELECTORS.comment);
comments.forEach(comment => {
comment.style.display = "";
let commentTextContainer = comment.querySelector(SELECTORS.commentContent);
if (commentTextContainer) {
commentTextContainer.style.backgroundColor = "";
}
});
console.log("Filter cleared, all comments visible.");
}
function findDateElement(comment) {
let signature = comment.querySelector(SELECTORS.signature);
if (!signature) return null;
let dateLinks = signature.querySelectorAll("a");
return dateLinks.length >= 2 ? dateLinks[1] : null;
}
function parseCommentDate(dateText) {
const months = {
"января": "January", "февраля": "February", "марта": "March",
"апреля": "April", "мая": "May", "июня": "June",
"июля": "July", "августа": "August", "сентября": "September",
"октября": "October", "ноября": "November", "декабря": "December"
};
if (dateText.startsWith("вчера в")) {
let match = dateText.match(/вчера в (\d{1,2}):(\d{2})/);
if (match) {
let yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
yesterday.setHours(parseInt(match[1], 10), parseInt(match[2], 10), 0, 0);
return yesterday;
}
}
if (dateText.startsWith("сегодня в")) {
let match = dateText.match(/сегодня в (\d{1,2}):(\d{2})/);
if (match) {
let today = new Date();
today.setHours(parseInt(match[1], 10), parseInt(match[2], 10), 0, 0);
return today;
}
}
let oldFormatMatch = dateText.match(/(\d{1,2})\s([а-яА-Я]+)\sв\s(\d{1,2}):(\d{2})/);
if (oldFormatMatch) {
let [_, day, monthName, hour, minute] = oldFormatMatch;
let month = months[monthName];
if (!month) return NaN;
let currentYear = new Date().getFullYear();
return new Date(`${day} ${month} ${currentYear} ${hour}:${minute}`);
}
let newFormatMatch = dateText.match(/(\d{2})\.(\d{2})\.(\d{4})\s(\d{2}):(\d{2})/);
if (newFormatMatch) {
let [_, day, month, year, hour, minute] = newFormatMatch;
return new Date(`${year}-${month}-${day}T${hour}:${minute}:00`);
}
return NaN;
}
function runScript() {
if (isPostPage()) {
let existingContainer = document.getElementById("comment-filter-container");
if (existingContainer) {
existingContainer.remove();
}
clearFilter();
observeForLinksSection();
}
}
// Run the script initially if on a post page
runScript();
// Listen for URL changes via popstate (back/forward navigation)
window.addEventListener('popstate', runScript);
// Listen for hash changes if navigation uses URL fragments
window.addEventListener('hashchange', runScript);
// Override pushState to detect URL changes
const originalPushState = history.pushState;
history.pushState = function () {
originalPushState.apply(this, arguments);
runScript();
};
// Override replaceState similarly
const originalReplaceState = history.replaceState;
history.replaceState = function () {
originalReplaceState.apply(this, arguments);
runScript();
};
})();