// ==UserScript==
// @name Panel Control XFilter 2.4.3 (c) tapeavion
// @namespace http://tampermonkey.net/
// @version 2.4.3
// @description Hide posts by keywords with the dashboard and hide posts from verified accounts
// @author gullampis810
// @match https://x.com/*
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// @icon https://www.pinclipart.com/picdir/big/450-4507608_twitter-circle-clipart.png
// ==/UserScript==
(function () {
"use strict";
// ===== Настройки и инициализация ===== //
const STORAGE_KEY = "hiddenKeywords";
console.log("STORAGE_KEY =", STORAGE_KEY);
let hiddenKeywords = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
let hideVerifiedAccounts = false; // Скрывать подтвержденные аккаунты
let hideNonVerifiedAccounts = JSON.parse(localStorage.getItem("hideNonVerifiedAccounts")) || false; // По умолчанию выключено
const languageFilters = {
english: /[a-zA-Z]/,
russian: /[А-Яа-яЁё]/,
japanese: /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/u,
ukrainian: /[А-Яа-яІіЄєЇїҐґ]/,
belarusian: /[А-Яа-яЎўЁёІі]/,
tatar: /[А-Яа-яӘәӨөҮүҖҗ]/,
mongolian: /[\p{Script=Mongolian}]/u,
chinese: /[\p{Script=Han}]/u,
german: /[a-zA-ZßÄäÖöÜü]/,
polish: /[a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/,
french: /[a-zA-Zàâçéèêëîïôûùüÿ]/,
swedish: /[a-zA-ZåäöÅÄÖ]/,
estonian: /[a-zA-ZäõöüÄÕÖÜ]/,
danish: /[a-zA-Z帿ŨÆ]/,
turkish: /[a-zA-ZıİçÇğĞöÖşŞüÜ]/,
portuguese: /[a-zA-Zàáâãçéêíóôõúü]/,
};
let activeLanguageFilters = {}; // Словарь активных языков
// ===== Сохранение в localStorage ===== //
function saveKeywords() {
console.log("saveKeywords called with", ...arguments);
localStorage.setItem(STORAGE_KEY, JSON.stringify(hiddenKeywords));
}
// ===== Функция для обновления отображения по языкам ===== //
function updateLanguageFilter(language) {
if (activeLanguageFilters[language]) {
delete activeLanguageFilters[language];
} else {
activeLanguageFilters[language] = languageFilters[language];
}
hidePosts(); // Пересчитываем скрытие постов
}
// ===== Проверка языка текста ===== //
function isTextInLanguage(text) {
for (const [language, regex] of Object.entries(activeLanguageFilters)) {
if (regex.test(text)) {
return true; // Возвращаем true, если текст на этом языке
}
}
return false;
}
// ===== Функция Скрытия постов ===== //
function hidePosts() {
document.querySelectorAll("article").forEach((article) => {
const textContent = article.innerText.toLowerCase();
const isVerifiedAccount = article.querySelector('[data-testid="icon-verified"]');
const matchesKeyword = hiddenKeywords.some((keyword) => {
try {
return new RegExp(keyword, "i").test(textContent);
} catch (e) {
return textContent.includes(keyword.toLowerCase()); // Фallback на обычное совпадение
}
});
// Проверяем, нужно ли скрывать пост
const shouldHideVerified = hideVerifiedAccounts && isVerifiedAccount;
const shouldHideNonVerified = hideNonVerifiedAccounts && !isVerifiedAccount;
const shouldHideByLanguage = isTextInLanguage(textContent);
const shouldHideByKeyword = matchesKeyword;
if (shouldHideVerified || shouldHideNonVerified || shouldHideByLanguage || shouldHideByKeyword) {
article.style.display = "none";
} else {
article.style.display = ""; // Показываем пост, если ни одно условие не выполнено
}
});
}
// ===== Создание панели управления ===== //
const panel = document.createElement("div");
panel.style.position = "fixed";
panel.style.bottom = "62px";
panel.style.right = "180px";
panel.style.width = "335px";
panel.style.height = "310px";
panel.style.padding = "8px";
panel.style.fontFamily = "Arial, sans-serif";
panel.style.backgroundColor = "#34506c";
panel.style.color = "#fff";
panel.style.borderRadius = "8px";
panel.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)";
panel.style.zIndex = "9999";
panel.style.overflow = "auto";
panel.style.transition = "height 0.3s ease";
panel.innerHTML = `
<h3 style="margin: 0; font-size: 16px;">Hiding Control</h3>
<input id="keywordInput" type="text" placeholder="Enter the word" style="width: calc(100% - 95px); height: 30px; padding: 5px; margin: 10px 0; border-radius: 5px; border: none;">
<div style="display: flex; flex-wrap: wrap; gap: 5px; position: relative;">
<button id="addKeyword" style="flex: 1; min-width: 70px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
Add it
</button>
<button id="exportKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
Export
</button>
<button id="importKeywords" style="flex: 1; min-width: 60px; max-width: 70px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px;">
Import
</button>
<button id="clearKeywords" style="flex: 1; min-width: 60px; max-width: 80px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; bottom: 0px; position: relative; left: 1px;">
Clear all
</button>
<button id="toggleVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
Hide verified accounts: Click to Disable
</button>
<button id="toggleNonVerifiedPosts" style="width: 242px; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
Hide non-verified accounts: Turn ON
</button>
<button id="openLanguagePopup" style="width: 80px; display: flex; align-content: center; flex-wrap: wrap; padding: 8px; background: #203142; color: white; border: none; border-radius: 5px; height: 40px; font-size: 13px; font-weight: bold;">
Language Filtering
</button>
</div>
<ul id="keywordList" style="list-style: position: relative; inside; padding: 0; margin-top: 10px; font-size: 14px; color: #fff;"></ul>
`;
document.body.appendChild(panel);
console.log("panel =", panel);
const lengthFilterInput = document.createElement("input");
lengthFilterInput.type = "number";
lengthFilterInput.placeholder = "Min length";
lengthFilterInput.style.width = "80px";
lengthFilterInput.style.marginTop = "10px";
panel.appendChild(lengthFilterInput);
lengthFilterInput.addEventListener("change", () => {
const minLength = parseInt(lengthFilterInput.value) || 0;
debouncedHidePosts();
});
// ===== Перетаскивание панели ===== //
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
// Функция для начала перетаскивания
panel.addEventListener("mousedown", (event) => {
isDragging = true;
offsetX = event.clientX - panel.offsetLeft;
offsetY = event.clientY - panel.offsetTop;
panel.style.cursor = "grabbing";
});
// Функция для перемещения панели
document.addEventListener("mousemove", (event) => {
if (isDragging) {
panel.style.left = event.clientX - offsetX + "px";
panel.style.top = event.clientY - offsetY + "px";
}
});
// Функция для завершения перетаскивания
document.addEventListener("mouseup", () => {
isDragging = false;
panel.style.cursor = "grab";
});
document.body.appendChild(panel);
console.log("panel =", panel);
// Создаем попап для выбора языков
const languagePopup = document.createElement("div");
console.log("languagePopup =", languagePopup);
languagePopup.style.display = "none";
languagePopup.style.position = "fixed";
languagePopup.style.top = "460px";
languagePopup.style.right = "65px";
languagePopup.style.transform = "translate(-52%, 7%)";
languagePopup.style.backgroundColor = "rgb(52, 80, 108)";
languagePopup.style.padding = "20px";
languagePopup.style.borderRadius = "8px";
languagePopup.style.zIndex = "10000";
languagePopup.style.width = "8%";
languagePopup.style.boxShadow = "rgba(0, 0, 0, 0.5) 0px 0px 10px";
languagePopup.style.fontFamily = "Arial, sans-serif"; // Устанавливаем шрифт Arial
// Добавляем чекбоксы для каждого языка
for (const language in languageFilters) {
const checkbox = document.createElement("input");
console.log("checkbox =", checkbox);
checkbox.type = "checkbox";
checkbox.id = `lang-${language}`;
checkbox.name = language;
const label = document.createElement("label");
console.log("label =", label);
label.htmlFor = `lang-${language}`;
label.textContent = language.charAt(0).toUpperCase() + language.slice(1);
const wrapper = document.createElement("div");
console.log("wrapper =", wrapper);
wrapper.appendChild(checkbox);
wrapper.appendChild(label);
languagePopup.appendChild(wrapper);
}
// Добавляем кнопку закрытия попапа
const closeButton = document.createElement("button");
console.log("closeButton =", closeButton);
closeButton.textContent = "X";
// Стили для круглой кнопки
closeButton.style.position = "relative";
closeButton.style.width = "40px";
closeButton.style.height = "40px";
closeButton.style.borderRadius = "50%";
closeButton.style.backgroundColor = "rgb(32, 49, 66)";
closeButton.style.color = "rgb(255, 255, 255)";
closeButton.style.border = "none";
closeButton.style.display = "flex";
closeButton.style.alignItems = "center";
closeButton.style.justifyContent = "center";
closeButton.style.cursor = "pointer";
closeButton.style.marginTop = "10px";
closeButton.style.boxShadow = "rgba(0, 0, 0, 0.1) 0px 4px 6px";
closeButton.style.transition = "background-color 0.3s";
closeButton.style.fontFamily = "Arial, sans-serif";
closeButton.style.left = "82%";
closeButton.style.top = "56px";
closeButton.addEventListener("click", () => {
languagePopup.style.display = "none";
});
languagePopup.appendChild(closeButton);
document.body.appendChild(languagePopup);
// Обработчик для открытия попапа
document.getElementById("openLanguagePopup").addEventListener("click", () => {
languagePopup.style.display = "block";
});
// Добавляем текст с предупреждением
const warningText = document.createElement("div");
console.log("warningText =", warningText);
warningText.textContent = "⚠️it may stops working";
warningText.style.color = "#ffcc00"; // Желтый цвет для предупреждения
warningText.style.fontSize = "14px";
warningText.style.marginBottom = "10px";
warningText.style.textAlign = "end";
warningText.style.right = "38px"; // Сдвигаем вправо на 50px
warningText.style.position = "relative"; // Устанавливаем позиционирование
warningText.style.top = "20px"; // Сдвигаем вниз на 15px
languagePopup.appendChild(warningText);
// Обработчики для чекбоксов
for (const language in languageFilters) {
document
.getElementById(`lang-${language}`)
.addEventListener("change", (event) => {
updateLanguageFilter(language);
});
}
// Стили для подсветки
const style = document.createElement("style");
console.log("style =", style);
style.textContent = `
button {
transition: box-shadow 0.3s, transform 0.3s;
}
button:hover {
box-shadow: 0 0 10px rgba(255, 255, 255, 0.7);
}
button:active {
transform: scale(0.95);
box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
}
::-webkit-scrollbar {
width: 25px; /* Ширина скроллбара */
}
::-webkit-scrollbar-thumb {
background-color: #C1A5EF; /* Цвет бегунка */
border-radius: 8px; /* Скругление бегунка */
border: 3px solid #4F3E6A; /* Внутренний отступ (цвет трека) */
height: 80px; /* Высота бегунка */
}
::-webkit-scrollbar-thumb:hover {
background-color: #C6AEFF; /* Цвет бегунка при наведении */
}
::-webkit-scrollbar-thumb:active {
background-color: #B097C9; /* Цвет бегунка при активном состоянии */
}
::-webkit-scrollbar-track {
background:rgba(69, 85, 101, 0); /* Цвет трека */
border-radius: 0px 0px 8px 0px; /* Скругление только нижнего правого угла */
}
::-webkit-scrollbar-track:hover {
background-color:rgba(69, 85, 101, 0); /* Цвет трека при наведении */
}
::-webkit-scrollbar-track:active {
background-color:rgba(69, 85, 101, 0); /* Цвет трека при активном состоянии */
}
`;
document.head.appendChild(style);
// скроллбар for Twitter X com //
// ======== Кнопка iOS-переключатель Panel FilterX ========= //
// Проверяем сохраненное состояние переключателя в localStorage
let isSwitchOn = localStorage.getItem("isSwitchOff") === "true"; // Начальное состояние переключателя из localStorage
// Создание элементов панели
const toggleButton = document.createElement("div");
console.log("toggleButton =", toggleButton);
toggleButton.style.position = "fixed";
toggleButton.style.top = "94%";
toggleButton.style.right = "90px";
toggleButton.style.width = "192px";
toggleButton.style.display = "flex";
toggleButton.style.alignItems = "center";
toggleButton.style.gap = "10px";
toggleButton.style.zIndex = "1";
toggleButton.style.background = "#15202b";
toggleButton.style.border = "4px solid #6c7e8e";
toggleButton.style.borderRadius = "18px";
toggleButton.style.boxSizing = "border-box";
// Создаем label для переключателя
const toggleLabel = document.createElement("label");
console.log("toggleLabel =", toggleLabel);
toggleLabel.style.display = "inline-block";
toggleLabel.style.width = "50px";
toggleLabel.style.height = "25px";
toggleLabel.style.borderRadius = "25px";
toggleLabel.style.backgroundColor = "#0d1319";
toggleLabel.style.position = "relative";
toggleLabel.style.cursor = "pointer";
toggleLabel.style.transition = "background-color 0.3s";
// Создаем кружок переключателя
const toggleSwitch = document.createElement("div");
console.log("toggleSwitch =", toggleSwitch);
toggleSwitch.style.position = "absolute";
toggleSwitch.style.width = "21px";
toggleSwitch.style.height = "21px";
toggleSwitch.style.borderRadius = "50%";
toggleSwitch.style.backgroundColor = "#6c7e8e";
toggleSwitch.style.top = "2px";
// Устанавливаем начальное положение кружка в зависимости от состояния
toggleSwitch.style.left = isSwitchOn ? "calc(100% - 23px)" : "2px"; // Если выключено, то слева, если включено — справа
// Плавная анимация
toggleSwitch.style.transition = "left 0.3s ease";
toggleSwitch.style.boxShadow = "rgb(21, 32, 43) -1px 1px 4px 1px";
// Устанавливаем начальное состояние фона
toggleLabel.style.backgroundColor = isSwitchOn ? "#425364" : "#0d1319";
// Функция для изменения состояния переключателя
function toggleSwitchState() {
isSwitchOn = !isSwitchOn;
localStorage.setItem("isSwitchOn", isSwitchOn.toString()); // Сохраняем состояние в localStorage (как строку)
// Обновляем стиль переключателя
toggleSwitch.style.left = isSwitchOn ? "calc(100% - 23px)" : "2px";
toggleLabel.style.backgroundColor = isSwitchOn ? "#425364" : "#0d1319";
}
// Добавляем обработчик на клик по переключателю
toggleButton.addEventListener("click", toggleSwitchState);
// Добавляем элементы на страницу
toggleLabel.appendChild(toggleSwitch);
toggleButton.appendChild(toggleLabel);
document.body.appendChild(toggleButton);
// Добавление текста к переключателю
const toggleText = document.createElement("span");
console.log("toggleText =", toggleText);
toggleText.style.position = "relative";
toggleText.style.right = "5px";
toggleText.textContent = "Panel FilterX";
toggleText.style.color = "#6c7e8e";
toggleText.style.fontFamily = "Arial, sans-serif";
toggleText.style.fontSize = "16px";
toggleText.style.marginLeft = "10px";
toggleText.style.fontWeight = "bold";
toggleButton.appendChild(toggleText);
//====================== Управление высотой панели =======================//
let isPanelVisible = localStorage.getItem("panelVisible") === "truefalse"; // По умолчанию скрыта, если в localStorage не сохранено другое значение
function togglePanel() {
if (isPanelVisible) {
panel.style.height = "0px"; // Сворачиваем панель
setTimeout(() => {
panel.style.display = "none"; // Скрываем панель после анимации
}, 300);
} else {
panel.style.display = "block"; // Показываем панель
setTimeout(() => {
panel.style.height = "310px"; // Разворачиваем панель
}, 10);
}
isPanelVisible = !isPanelVisible; // Переключаем состояние
localStorage.setItem("panelVisible", isPanelVisible.toString()); // Сохраняем состояние
}
toggleButton.addEventListener("click", togglePanel);
// При загрузке восстанавливаем состояние панели
if (isPanelVisible) {
panel.style.height = "310px"; // Разворачиваем панель
panel.style.display = "block";
} else {
panel.style.height = "0px"; // Сворачиваем панель
panel.style.display = "none";
}
const keywordInput = document.getElementById("keywordInput");
const addKeywordBtn = document.getElementById("addKeyword");
const clearKeywordsBtn = document.getElementById("clearKeywords");
const exportKeywordsBtn = document.getElementById("exportKeywords");
const importKeywordsBtn = document.getElementById("importKeywords");
const toggleVerifiedBtn = document.getElementById("toggleVerifiedPosts");
const toggleNonVerifiedBtn = document.getElementById("toggleNonVerifiedPosts");
const openLanguagePopupBtn = document.getElementById("openLanguagePopup");
const keywordList = document.getElementById("keywordList");
// ===== Обработчики событий ===== //
addKeywordBtn.addEventListener("click", () => {
const keyword = keywordInput.value.trim();
if (keyword && !hiddenKeywords.includes(keyword)) {
hiddenKeywords.push(keyword);
saveKeywords();
updateKeywordList();
debouncedHidePosts();
keywordInput.value = "";
}
});
// Устанавливаем начальный текст кнопки в зависимости от сохраненного состояния
toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`;
// Обработчик клика по кнопке
toggleNonVerifiedBtn.addEventListener("click", () => {
hideNonVerifiedAccounts = !hideNonVerifiedAccounts;
localStorage.setItem("hideNonVerifiedAccounts", JSON.stringify(hideNonVerifiedAccounts)); // Сохраняем состояние
toggleNonVerifiedBtn.textContent = `Hide non-verified accounts: ${hideNonVerifiedAccounts ? "Turn OFF" : "Turn ON"}`;
hidePosts(); // Перепроверяем все посты с новыми настройками
});
document.getElementById("clearKeywords").addEventListener("click", () => {
if (confirm("Are you sure you want to clear the list?")) {
hiddenKeywords = [];
saveKeywords();
updateKeywordList();
hidePosts();
}
});
document.getElementById("exportKeywords").addEventListener("click", () => {
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(hiddenKeywords)
)}`;
console.log('dataStr =', dataStr); // Лог вне строки
const downloadAnchor = document.createElement("a");
console.log("downloadAnchor =", downloadAnchor);
downloadAnchor.setAttribute("href", dataStr);
downloadAnchor.setAttribute("download", "hidden_keywords.json");
document.body.appendChild(downloadAnchor);
downloadAnchor.click();
document.body.removeChild(downloadAnchor);
});
document.getElementById("importKeywords").addEventListener("click", () => {
const input = document.createElement("input");
console.log("input =", input);
input.type = "file";
input.accept = "application/json";
input.addEventListener("change", (event) => {
const file = event.target.files[0];
console.log("file =", file);
const reader = new FileReader();
console.log("reader =", reader);
reader.onload = () => {
try {
const importedKeywords = JSON.parse(reader.result);
console.log("importedKeywords =", importedKeywords);
if (Array.isArray(importedKeywords)) {
hiddenKeywords = [
...new Set([...hiddenKeywords, ...importedKeywords]),
];
saveKeywords();
updateKeywordList();
hidePosts();
} else {
alert("Incorrect file format.");
}
} catch (e) {
alert("Error reading the file.");
}
};
reader.readAsText(file);
});
input.click();
});
document.getElementById("openLanguagePopup").addEventListener("click", () => {
const panelRect = panel.getBoundingClientRect();
languagePopup.style.top = `${panelRect.top - 320}px`; // Чуть выше панели
languagePopup.style.left = `${panelRect.right - 10}px`; // Справа от панели
languagePopup.style.display = "block";
});
// Переменная для отслеживания клика на кнопку "Перевести пост"
let isTranslateButtonClicked = false;
// Функция для отслеживания клика по кнопке "Перевести пост"
function handleTranslateButtonClick(event) {
isTranslateButtonClicked = true;
setTimeout(() => { isTranslateButtonClicked = false; }, 500); // Сбросить флаг через 500 мс
}
// ===== Наблюдатель за кнопками "Перевести пост" ===== //
function observeTranslateButton() {
document.querySelectorAll('button[aria-expanded="false"]').forEach(button => {
button.removeEventListener('click', handleTranslateButtonClick); // Убираем старые обработчики
button.addEventListener('click', handleTranslateButtonClick); // Добавляем новый обработчик
});
}
// ===== Кнопка для включения/выключения скрытия подтвержденных аккаунтов ===== //
document
.getElementById("toggleVerifiedPosts")
.addEventListener("click", () => {
hideVerifiedAccounts = !hideVerifiedAccounts;
document.getElementById("toggleVerifiedPosts").textContent =
`Hide verified accounts: ${
hideVerifiedAccounts ? "Turn OFF" : "Turn ON"
}`;
hidePosts(); // Перепроверка всех постов с новыми настройками
});
// ===== Функция для применения кастомного скроллбара ===== //
function applyCustomScrollbar() {
const style = document.createElement("style");
style.id = "custom-scrollbar-style";
style.innerHTML = `
#keywordList::-webkit-scrollbar {
width: 25px; /* Ширина скроллбара */
}
#keywordList::-webkit-scrollbar-thumb {
background-color: #C1A5EF; /* Цвет бегунка */
border-radius: 8px; /* Скругление бегунка */
border: 3px solid #4F3E6A; /* Внутренний отступ (цвет трека) */
height: 80px; /* Высота бегунка */
}
#keywordList::-webkit-scrollbar-thumb:hover {
background-color: #C6AEFF; /* Цвет бегунка при наведении */
}
#keywordList::-webkit-scrollbar-thumb:active {
background-color: #B097C9; /* Цвет бегунка при активном состоянии */
}
#keywordList::-webkit-scrollbar-track {
background: #455565; /* Цвет трека */
border-radius: 0px 0px 8px 0px; /* Скругление только нижнего правого угла */
}
#keywordList::-webkit-scrollbar-track:hover {
background-color: #455565; /* Цвет трека при наведении */
}
#keywordList::-webkit-scrollbar-track:active {
background-color: #455565; /* Цвет трека при активном состоянии */
}
`;
if (!document.getElementById("custom-scrollbar-style")) {
document.head.appendChild(style);
}
}
// ===== Функция для наблюдения за изменениями в списке ===== //
function observeKeywordList() {
const list = document.getElementById("keywordList");
if (!list) return;
const observer = new MutationObserver(() => {
console.log(
"Обнаружены изменения в списке ключевых слов, обновляем стили...",
);
applyCustomScrollbar();
});
observer.observe(list, { childList: true, subtree: true });
}
// ===== Обновление списка ключевых слов ===== //
function updateKeywordList() {
const list = document.getElementById("keywordList");
console.log("list =", list);
list.style.listStyle = "inside";
list.style.padding = "0px 10px 0px 0px";
list.style.marginTop = "10px";
list.style.fontSize = "14px";
list.style.color = "rgb(255, 255, 255)";
list.style.maxHeight = "135px";
list.style.overflowY = "auto";
list.style.border = "1px solid rgb(204, 204, 204)";
list.style.borderRadius = "5px";
list.style.backgroundColor = "rgb(21, 32, 43)";
list.style.boxShadow = "rgba(0, 0, 0, 0.3) 0px 2px 5px";
list.style.position = "relative";
list.style.width = "315px";
list.innerHTML = "";
hiddenKeywords.forEach((keyword, index) => {
const listItem = document.createElement("li");
console.log("listItem =", listItem);
listItem.textContent = keyword;
listItem.style.marginBottom = "5px";
const deleteButton = document.createElement("button");
console.log("deleteButton =", deleteButton);
deleteButton.textContent = "❌";
deleteButton.style.marginLeft = "10px";
deleteButton.style.backgroundColor = "#f44336";
deleteButton.style.color = "#fff";
deleteButton.style.border = "none";
deleteButton.style.borderRadius = "3px";
deleteButton.style.cursor = "pointer";
deleteButton.addEventListener("click", () => {
hiddenKeywords.splice(index, 1);
saveKeywords();
updateKeywordList();
hidePosts();
});
listItem.appendChild(deleteButton);
list.appendChild(listItem);
});
if (hiddenKeywords.length === 0) {
list.textContent = "Нет";
}
applyCustomScrollbar(); // Применяем кастомный скроллбар
observeKeywordList(); // Повторно запускаем наблюдатель
}
// Запуск кастомного скролла и наблюдателя при загрузке страницы
applyCustomScrollbar();
observeKeywordList();
// ===== Инициализация ===== //
updateKeywordList(); // Обновление списка при загрузке страницы
hidePosts();
function debounce(func, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const debouncedHidePosts = debounce(hidePosts, 200); // 200 мс задержки
// ===== Наблюдатель для динамического контента ===== //
// Используем debounced версию
const observer = new MutationObserver(() => {
debouncedHidePosts();
observeTranslateButton();
});
observer.observe(document.body, { childList: true, subtree: true });
// ==== автоматического скрытия панели управления и кнопки переключателя (при открытии просмотра фото ======= //
// Функция для проверки, открыто ли модальное окно с фото
function isPhotoViewerOpen() {
// Проверяем URL на наличие /photo/
const currentUrl = window.location.href;
const isPhotoOpen = /\/photo\/\d+$/.test(currentUrl);
// Дополнительно проверяем наличие модального окна (если URL-метод ненадежен)
const photoModal = document.querySelector('div[aria-label="Image"]') || document.querySelector('div[data-testid="imageViewer"]');
return isPhotoOpen || !!photoModal;
}
// Функция для скрытия/показа панели и кнопки
function toggleElementsVisibility() {
const isPhotoOpen = isPhotoViewerOpen();
if (isPhotoOpen) {
// Скрываем панель и кнопку
panel.style.display = "none";
toggleButton.style.display = "none";
} else {
// Восстанавливаем видимость, если панель должна быть видима
if (isPanelVisible) {
panel.style.display = "block";
panel.style.height = "310px";
}
toggleButton.style.display = "flex"; // Возвращаем кнопку
}
}
// Начальная проверка при загрузке
toggleElementsVisibility();
// Отслеживание изменений URL через popstate (для навигации вперед/назад)
window.addEventListener("popstate", () => {
toggleElementsVisibility();
});
// Отслеживание изменений в DOM (для динамической загрузки контента)
const urlObserver = new MutationObserver(() => {
toggleElementsVisibility();
});
// Наблюдаем за изменениями в document.body
urlObserver.observe(document.body, { childList: true, subtree: true });
// Дополнительно: отслеживаем клики по элементам, которые могут закрыть модальное окно
document.addEventListener("click", (event) => {
// Проверяем, если кликнули на элемент закрытия модального окна
if (event.target.closest('div[aria-label="Close"]') || event.target.closest('div[data-testid="imageViewer-close"]')) {
setTimeout(() => {
toggleElementsVisibility();
}, 100); // Небольшая задержка, чтобы дождаться закрытия модального окна
}
});
})();