您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Some useful utilities for Lolzteam
当前为
// ==UserScript== // @name LZT Local Uniq // @version 1.0.7 // @description Some useful utilities for Lolzteam // @description:ru Локальный уник // @icon https://cdn.jsdelivr.net/gh/ilyhalight/[email protected]/public/static/img/lzt-upgrade-mini.png // @author Kain // @license MIT // @namespace lztlocaluniq // @match https://lolz.live/* // @match https://zelenka.guru/* // @match https://lolz.guru/* // @match https://lzt.market/* // @connect greasyfork.org // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_info // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @run-at document-start // ==/UserScript== (function() { 'use strict'; // ====================КОНФИГУРАЦИЯ ==================== const MEGA_CONFIG = { VERSION: '1.0', UPDATE_INTERVAL: 100, OBSERVER_DEBOUNCE: 100, MAX_RETRIES: 10, DEBUG: true, THEMES: { DARK: { name: 'Тёмная', bg: '#1a1a1a', card: '#2a2a2a', text: '#ffffff', accent: '#ff6b6b', secondary: '#4ecdc4', border: '#444', glow: '0 0 30px rgba(255, 107, 107, 0.6)' }, BLUE: { name: 'Синяя', bg: '#0a1929', card: '#1a2b3c', text: '#e1f5fe', accent: '#2196f3', secondary: '#00bcd4', border: '#1565c0', glow: '0 0 30px rgba(33, 150, 243, 0.6)' }, GREEN: { name: 'Зелёная', bg: '#0d1f0d', card: '#1a2e1a', text: '#e8f5e8', accent: '#4caf50', secondary: '#8bc34a', border: '#388e3c', glow: '0 0 30px rgba(76, 175, 80, 0.6)' }, PURPLE: { name: 'Фиолетовая', bg: '#1a0d2e', card: '#2a1a3c', text: '#f3e5f5', accent: '#9c27b0', secondary: '#ba68c8', border: '#7b1fa2', glow: '0 0 30px rgba(156, 39, 176, 0.6)' }, PINK: { name: 'Розовая', bg: '#2e0d1a', card: '#3c1a2a', text: '#fce4ec', accent: '#e91e63', secondary: '#f06292', border: '#ad1457', glow: '0 0 30px rgba(233, 30, 99, 0.6)' }, NEON: { name: 'Неоновая', bg: '#0a0a0a', card: '#1a1a1a', text: '#00ff00', accent: '#00ffff', secondary: '#ff00ff', border: '#00ff00', glow: '0 0 40px rgba(0, 255, 255, 0.8)' }, GOLD: { name: 'Золотая', bg: '#1a1200', card: '#2a1a00', text: '#ffd700', accent: '#ffb300', secondary: '#ff9800', border: '#b8860b', glow: '0 0 35px rgba(255, 215, 0, 0.7)' } }, GLOW_EFFECTS: { NONE: { name: 'Нет', value: 'none' }, SOFT: { name: 'Мягкое', value: '0 0 20px rgba(255, 255, 255, 0.3)' }, ACCENT: { name: 'Акцентное', value: 'current' }, NEON: { name: 'Неоновое', value: '0 0 30px currentColor' }, INTENSE: { name: 'Интенсивное', value: '0 0 40px currentColor, 0 0 60px rgba(255, 255, 255, 0.2)' }, PULSATING: { name: 'Пульсирующее', value: '0 0 25px currentColor' } }, ICONS: { } }; // ==================== СИСТЕМА ЛОГГИРОВАНИЯ ==================== class MegaLogger { static log(...args) { if (MEGA_CONFIG.DEBUG) { console.log(`%c🔹 LZT MEGA v${MEGA_CONFIG.VERSION}:`, 'color: #4ecdc4; font-weight: bold', ...args); } } static error(...args) { console.error(`%c❌ LZT MEGA ERROR:`, 'color: #ff6b6b; font-weight: bold', ...args); } static warn(...args) { console.warn(`%c⚠️ LZT MEGA WARN:`, 'color: #ff9800; font-weight: bold', ...args); } static success(...args) { console.log(`%c✅ LZT MEGA SUCCESS:`, 'color: #4caf50; font-weight: bold', ...args); } } // ==================== СИСТЕМА ХРАНЕНИЯ ==================== class MegaStorage { static getSettings() { try { const settings = GM_getValue('megaUniq-settings'); if (settings) { MegaLogger.log('Настройки загружены из GM:', settings); return settings; } // Если нет настроек в GM, пробуем загрузить из localStorage (для обратной совместимости) return this.getSettingsFromLocalStorage(); } catch (e) { MegaLogger.error('Ошибка загрузки настроек:', e); return this.getDefaultSettings(); } } static saveSettings(settings) { try { // Сохраняем в GM (кросс-доменно) GM_setValue('megaUniq-settings', settings); // Также сохраняем в localStorage для обратной совместимости this.saveSettingsToLocalStorage(settings); MegaLogger.success('Настройки сохранены в GM хранилище'); return true; } catch (e) { MegaLogger.error('Ошибка сохранения настроек:', e); return false; } } // Методы для обратной совместимости с localStorage static getSettingsFromLocalStorage() { try { const settings = { myId: localStorage.getItem('megaUniq-myId') || '', myName: localStorage.getItem('megaUniq-myName') || '', username: localStorage.getItem('megaUniq-username') || '', banner: localStorage.getItem('megaUniq-banner') || '', usernameCss: localStorage.getItem('megaUniq-username-css') || '', bannerCss: localStorage.getItem('megaUniq-banner-css') || '', theme: localStorage.getItem('megaUniq-theme') || 'DARK', autoDetect: localStorage.getItem('megaUniq-autoDetect') !== 'false', lastUpdate: localStorage.getItem('megaUniq-lastUpdate') || '', usageCount: parseInt(localStorage.getItem('megaUniq-usageCount') || '0') }; // Если есть настройки в localStorage, переносим их в GM if (settings.myId || settings.myName) { this.saveSettings(settings); MegaLogger.log('Настройки мигрированы из localStorage в GM'); } return settings; } catch (e) { return this.getDefaultSettings(); } } static saveSettingsToLocalStorage(settings) { try { localStorage.setItem('megaUniq-myId', settings.myId || ''); localStorage.setItem('megaUniq-myName', settings.myName || ''); localStorage.setItem('megaUniq-username', settings.username || ''); localStorage.setItem('megaUniq-banner', settings.banner || ''); localStorage.setItem('megaUniq-username-css', settings.usernameCss || ''); localStorage.setItem('megaUniq-banner-css', settings.bannerCss || ''); localStorage.setItem('megaUniq-theme', settings.theme || 'DARK'); localStorage.setItem('megaUniq-autoDetect', settings.autoDetect ? 'true' : 'false'); localStorage.setItem('megaUniq-lastUpdate', new Date().toISOString()); localStorage.setItem('megaUniq-usageCount', (settings.usageCount || 0).toString()); } catch (e) { MegaLogger.error('Ошибка сохранения в localStorage:', e); } } static getDefaultSettings() { return { myId: '', myName: '', username: '', banner: 'LOLZTEAM', usernameCss: '', bannerCss: '', theme: 'DARK', autoDetect: true, lastUpdate: '', usageCount: 0 }; } static clearCache() { try { // Очищаем GM хранилище const values = GM_listValues(); values.forEach(key => { if (key.includes('megaUniq')) { GM_deleteValue(key); } }); // Очищаем localStorage const keys = Object.keys(localStorage).filter(key => key.startsWith('megaUniq-')); keys.forEach(key => localStorage.removeItem(key)); MegaLogger.log('Кэш очищен в GM и localStorage'); return keys.length; } catch (e) { MegaLogger.error('Ошибка очистки кэша:', e); return 0; } } static getStorageInfo() { try { const settings = GM_getValue('megaUniq-settings'); const size = settings ? JSON.stringify(settings).length : 0; return { keys: 1, size: size }; } catch (e) { return { keys: 0, size: 0 }; } } } // ==================== СИСТЕМА ОБРАБОТКИ DOM ==================== class MegaDOMProcessor { constructor(settings) { this.settings = settings; this.processedElements = new WeakSet(); this.retryCount = 0; this.lastProcessed = 0; } // ПРОЦЕССИНГ: applyMegaUniq() { // УСИЛЕННАЯ ПРОВЕРКА: требуем только myId и myName, остальное опционально if (!this.settings.myId || !this.settings.myName) { MegaLogger.warn('Не заданы обязательные настройки: ID и текущий ник'); return false; } const startTime = Date.now(); MegaLogger.log(`Запуск мега-обработки для: ${this.settings.myName} (ID: ${this.settings.myId})`); try { // Очищаем предыдущие обработки this.processedElements = new WeakSet(); // Последовательная обработка ВСЕХ элементов const processors = [ () => this.processUserLinks(), () => this.processBanners(), // () => this.processProfileBanners(), // () => this.processAvatars(), // () => this.processIcons(), // () => this.processTextNodes(), // () => this.processPageTitle(), // () => this.processRichContent(), // () => this.processMetaTags(), // () => this.processNavigation(), // () => this.processTooltips() ]; processors.forEach(processor => processor()); this.retryCount = 0; const duration = Date.now() - startTime; MegaLogger.success(`Мега-обработка завершена за ${duration}ms`); this.lastProcessed = Date.now(); return true; } catch (error) { MegaLogger.error('Критическая ошибка обработки:', error); this.retryCount++; if (this.retryCount <= MEGA_CONFIG.MAX_RETRIES) { setTimeout(() => this.applyMegaUniq(), 1000); } return false; } } // 1. ОБРАБОТКА ССЫЛОК И ЭЛЕМЕНТОВ ИМЕНИ processUserLinks() { const selectors = [ `a[href*="/members/${this.settings.myId}/"]`, `a[href*="/members/${this.settings.myId}?"]`, `a[href*="/members/${this.settings.myId}"]`, `a[href*="user_id=${this.settings.myId}"]`, `[href*="/members/${this.settings.myId}/"]`, `h1.username span`, `#NavigationAccountUsername span`, `.username span`, `.accountUsername span`, `[itemprop="name"] span`, `h1.username`, `#NavigationAccountUsername`, `.accountUsername`, `[itemprop="name"]` ]; selectors.forEach(selector => { try { document.querySelectorAll(selector).forEach(element => { if (this.processedElements.has(element)) return; if (element.tagName === 'A' || element.href) { this.processLinkElement(element); } else { this.processUsernameElement(element); } this.processedElements.add(element); }); } catch (e) { MegaLogger.error(`Ошибка селектора ${selector}:`, e); } }); document.querySelectorAll('a').forEach(link => { if (link.textContent.includes(this.settings.myName) && !this.processedElements.has(link)) { this.processLinkElement(link); this.processedElements.add(link); } }); this.processSpecialUsernameElements(); } processLinkElement(link) { const usernameText = this.settings.username || this.settings.myName; const finalText = this.settings.usernameIcon ? `${this.settings.usernameIcon} ${usernameText}` : usernameText; // Сохраняем оригинальную структуру const originalHTML = link.innerHTML; let newHTML = originalHTML; // Умная замена: ищем текст содержащий имя пользователя if (originalHTML.includes(this.settings.myName)) { // Заменяем имя пользователя, сохраняя окружающий текст newHTML = originalHTML.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), finalText ); } // Применяем изменения if (newHTML !== originalHTML) { link.innerHTML = newHTML; } // Применяем CSS - но теперь более аккуратно if (this.settings.usernameCss) { this.safeApplyStylesToUsernameInLink(link, this.settings.myName, finalText); } } // НОВЫЙ МЕТОД: Умное применение стилей только к имени в ссылке safeApplyStylesToUsernameInLink(link, oldName, newName) { // Ищем текстовые узлы, содержащие новое имя const walker = document.createTreeWalker( link, NodeFilter.SHOW_TEXT, null, false ); let textNode; const nodesToProcess = []; while (textNode = walker.nextNode()) { if (textNode.textContent.includes(newName)) { nodesToProcess.push(textNode); } } // Обрабатываем найденные узлы nodesToProcess.forEach(textNode => { const parent = textNode.parentNode; // Если родитель уже спан с нашими стилями - пропускаем if (parent.classList && parent.classList.contains('mega-username-styled')) { return; } // Разделяем текст на части до и после имени const text = textNode.textContent; const nameIndex = text.indexOf(newName); if (nameIndex !== -1) { const beforeText = text.substring(0, nameIndex); const afterText = text.substring(nameIndex + newName.length); // Создаем новые узлы const beforeNode = document.createTextNode(beforeText); const nameSpan = document.createElement('span'); const afterNode = document.createTextNode(afterText); // Настраиваем спан с именем nameSpan.className = 'mega-username-styled'; nameSpan.style.cssText = this.settings.usernameCss; nameSpan.textContent = newName; // Заменяем оригинальный текстовый узел const fragment = document.createDocumentFragment(); if (beforeText) fragment.appendChild(beforeNode); fragment.appendChild(nameSpan); if (afterText) fragment.appendChild(afterNode); parent.replaceChild(fragment, textNode); } }); } processUsernameElement(element) { const usernameText = this.settings.username || this.settings.myName; const finalText = this.settings.usernameIcon ? `${this.settings.usernameIcon} ${usernameText}` : usernameText; if (element.textContent.includes(this.settings.myName)) { if (element.tagName === 'SPAN') { const originalHTML = element.innerHTML; const newHTML = originalHTML.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), finalText ); if (newHTML !== originalHTML) { element.innerHTML = newHTML; } } else { element.textContent = element.textContent.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), finalText ); } if (this.settings.usernameCss) { this.safeApplyStyles(element, this.settings.usernameCss); } } } processSpecialUsernameElements() { const specialCases = [ { selector: 'h1.username', innerSelector: 'span' }, { selector: '#NavigationAccountUsername', innerSelector: 'span' }, { selector: '.accountUsername', innerSelector: 'span' } ]; specialCases.forEach(specialCase => { try { document.querySelectorAll(specialCase.selector).forEach(container => { if (this.processedElements.has(container)) return; const innerElement = container.querySelector(specialCase.innerSelector); if (innerElement && innerElement.textContent.includes(this.settings.myName)) { this.processUsernameElement(innerElement); this.processedElements.add(container); } }); } catch (e) { MegaLogger.error(`Ошибка обработки специального случая ${specialCase.selector}:`, e); } }); } // 2. ОБРАБОТКА ЛЫЧЕК processBanners() { const bannerSelectors = [ 'em.userBanner', '.userBanner', '.userBannerWrapper', '[class*="banner"]', '[class*="Banner"]', 'span.banner', 'div.banner' ]; bannerSelectors.forEach(selector => { try { document.querySelectorAll(selector).forEach(banner => { if (this.processedElements.has(banner)) return; // ⚠️ Пропускаем бейджи у аватарок if ( banner.classList.contains('avatarUserBadge') || banner.closest('.avatarUserBadge') ) return; if (this.isMyBanner(banner)) { this.processBannerElement(banner); this.processedElements.add(banner); } }); } catch (e) { MegaLogger.error(`Ошибка обработки баннеров ${selector}:`, e); } }); } // НОВЫЙ МЕТОД: Обработка лычек на странице профиля processProfileBanners() { if (!window.location.href.includes('/members/')) return; const profileSelectors = [ '.mainProfileHeader .userBanner', '.profilePage .userBanner', '.userDetails .userBanner', '[class*="profile"] .userBanner', '.userBanner' ]; profileSelectors.forEach(selector => { try { document.querySelectorAll(selector).forEach(banner => { if (this.processedElements.has(banner)) return; // ⚠️ Пропускаем бейджи у аватарок в профиле if ( banner.classList.contains('avatarUserBadge') || banner.closest('.avatarUserBadge') ) return; if (window.location.href.includes(`/members/${this.settings.myId}/`)) { MegaLogger.log('Найдена лычка в профиле:', banner); this.processBannerElement(banner); this.processedElements.add(banner); } }); } catch (e) { MegaLogger.error(`Ошибка обработки профильных баннеров ${selector}:`, e); } }); } isMyBanner(banner) { const myId = String((this.settings && this.settings.myId) || '').trim(); const myName = (this.settings && this.settings.myName) ? String(this.settings.myName).trim() : ''; if (!myId) { // если myId не задан — не показываем лычку console.warn('isMyBanner: myId not set in settings', this.settings); return false; } // 1) если мы на странице профиля вида /members/<id>/ или ..<id> const url = window.location.href || ''; if (url.includes(`/members/${myId}/`) || url.match(new RegExp(`[\\./]${myId}(?:/|$)`))) { return true; } // вспомогательная функция: проверяет элемент на наличие ссылки/атрибута нашего id или имени function elementHasMyLink(el) { if (!el) return false; // 1) элементы с data-user-id const dataEls = el.querySelectorAll('[data-user-id]'); for (const e of dataEls) { if (String(e.getAttribute('data-user-id')) === myId) return true; } // 2) перебираем ссылки/якоря в контейнере const anchors = el.querySelectorAll('a[href], a'); for (const a of anchors) { const href = a.getAttribute('href') || ''; const dataUid = a.getAttribute('data-user-id') || (a.dataset && a.dataset.userId); if (dataUid && String(dataUid) === myId) return true; if (href.indexOf(`/members/${myId}/`) !== -1) return true; // матч на формат username.12345 (часто у XenForo в URL профиля) if (href.match(new RegExp(`\\.${myId}(?:/|$)`))) return true; if (myName && a.textContent && a.textContent.trim() === myName) return true; const dname = a.getAttribute('data-user-name') || (a.dataset && a.dataset.userName); if (myName && dname && dname === myName) return true; } return false; } // 2) типичные контейнеры рядом с баннером const container = banner.closest('.message, .messageUserInfo, .posterAvatar, .memberListItem, .userItem, .userList, .profilePage, .mainProfileHeader, .messageList'); if (elementHasMyLink(container)) return true; // 3) поднимаемся вверх по DOM до N уровней и проверяем let parent = banner.parentElement; for (let i = 0; i < 6 && parent; i++, parent = parent.parentElement) { if (elementHasMyLink(parent)) return true; } // не нашёл — чужая лычка return false; } processBannerElement(banner) { const bannerText = this.settings.banner || ''; const finalText = this.settings.bannerIcon ? `${this.settings.bannerIcon} ${bannerText}` : bannerText; MegaLogger.log('Обработка лычки:', banner, 'Текст:', finalText); // Ищем текстовый элемент внутри лычки let textElement = banner.querySelector('strong') || banner.querySelector('span') || banner; // Сохраняем оригинальные классы и структуру const originalClasses = textElement.className; const originalHTML = textElement.innerHTML; // Применяем текст textElement.textContent = finalText; textElement.className = originalClasses; // Восстанавливаем классы // Применяем CSS - очищаем старые стили и применяем новые if (this.settings.bannerCss) { banner.style.cssText = ''; this.safeApplyStyles(banner, this.settings.bannerCss); // Также применяем к внутренним элементам banner.querySelectorAll('strong, span').forEach(el => { el.style.cssText = ''; this.safeApplyStyles(el, this.settings.bannerCss); }); } MegaLogger.log('Лычка обработана:', banner); } // 3. ОБРАБОТКА АВАТАРОК processAvatars() { const avatarSelectors = [ '.avatar', '[class*="avatar"]', '.avatarScaler', '.userImg', '.profilePhoto', '[class*="userImg"]', '[class*="profilePhoto"]' ]; avatarSelectors.forEach(selector => { try { document.querySelectorAll(selector).forEach(avatar => { if (this.processedElements.has(avatar)) return; if (this.isMyAvatar(avatar)) { this.processAvatarElement(avatar); this.processedElements.add(avatar); } }); } catch (e) { MegaLogger.error(`Ошибка обработки аватарок ${selector}:`, e); } }); } isMyAvatar(avatar) { return (avatar.href && avatar.href.includes(`/members/${this.settings.myId}/`)) || (avatar.closest && avatar.closest(`a[href*="/members/${this.settings.myId}/"]`)) || (avatar.querySelector && avatar.querySelector(`a[href*="/members/${this.settings.myId}/"]`)); } processAvatarElement(avatar) { const usernameText = this.settings.username || this.settings.myName; const finalText = this.settings.avatarIcon ? `${this.settings.avatarIcon} ${usernameText}` : usernameText; if (this.settings.avatarIcon && !avatar.previousSibling?.textContent?.includes(this.settings.avatarIcon)) { const iconSpan = document.createElement('span'); iconSpan.textContent = this.settings.avatarIcon + ' '; iconSpan.style.marginRight = '5px'; avatar.parentNode.insertBefore(iconSpan, avatar); } if (this.settings.usernameCss) { this.safeApplyStyles(avatar, this.settings.usernameCss); } } // 4. ОБРАБОТКА ИКОНОК processIcons() { if (!this.settings.avatarIcon && !this.settings.bannerIcon && !this.settings.usernameIcon) return; document.querySelectorAll(`a[href*="/members/${this.settings.myId}/"]`).forEach(link => { if (this.settings.usernameIcon && !link.textContent.includes(this.settings.usernameIcon)) { link.innerHTML = this.settings.usernameIcon + ' ' + link.innerHTML; } }); } // 5. ОБРАБОТКА ТЕКСТОВЫХ УЗЛОВ processTextNodes() { try { const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { if (this.processedElements.has(node.parentElement) || node.parentElement.closest('a, .userBanner, .avatar, [class*="user"]')) { return NodeFilter.FILTER_REJECT; } return node.textContent.includes(this.settings.myName) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; } }, false ); let node; while ((node = walker.nextNode())) { if (node.textContent.trim() === this.settings.myName) { const finalText = this.settings.usernameIcon ? `${this.settings.usernameIcon} ${this.settings.username || this.settings.myName}` : this.settings.username || this.settings.myName; node.textContent = finalText; } } } catch (e) { MegaLogger.error('Ошибка обработки текстовых узлов:', e); } } // 6. ДОПОЛНИТЕЛЬНЫЕ ОБРАБОТКИ processPageTitle() { if (document.title.includes(this.settings.myName)) { document.title = document.title.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), this.settings.username || this.settings.myName ); } } processRichContent() { document.querySelectorAll('.messageText, .content, .postbody, .userContent').forEach(element => { if (element.textContent.includes(this.settings.myName) && !element.isContentEditable) { element.innerHTML = element.innerHTML.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), this.settings.username || this.settings.myName ); } }); } processMetaTags() { document.querySelectorAll('meta[name="description"], meta[property="og:title"]').forEach(meta => { if (meta.content.includes(this.settings.myName)) { meta.content = meta.content.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), this.settings.username || this.settings.myName ); } }); } processNavigation() { document.querySelectorAll('.breadcrumb, .navTabs, .pagination').forEach(nav => { if (nav.textContent.includes(this.settings.myName)) { nav.innerHTML = nav.innerHTML.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), this.settings.username || this.settings.myName ); } }); } processTooltips() { document.querySelectorAll('[title], [data-tip], [data-original-title]').forEach(el => { const title = el.getAttribute('title') || el.getAttribute('data-tip') || el.getAttribute('data-original-title'); if (title && title.includes(this.settings.myName)) { const newTitle = title.replace( new RegExp(this.escapeRegExp(this.settings.myName), 'g'), this.settings.username || this.settings.myName ); el.setAttribute('title', newTitle); if (el.getAttribute('data-tip')) el.setAttribute('data-tip', newTitle); if (el.getAttribute('data-original-title')) el.setAttribute('data-original-title', newTitle); } }); } // ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ deepReplaceHTML(html, oldText, newText) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const replaceInNode = (node) => { if (node.nodeType === Node.TEXT_NODE) { node.textContent = node.textContent.replace( new RegExp(`\\b${this.escapeRegExp(oldText)}\\b`, 'g'), newText ); } else { node.childNodes.forEach(replaceInNode); } }; replaceInNode(doc.body); return doc.body.innerHTML; } deepTextReplace(element, oldText, newText) { if (element.childNodes.length > 0) { element.childNodes.forEach(child => this.deepTextReplace(child, oldText, newText)); } else if (element.nodeType === Node.TEXT_NODE) { element.textContent = element.textContent.replace(new RegExp(this.escapeRegExp(oldText), 'g'), newText); } } safeApplyStyles(element, css) { try { // Очищаем старые стили перед применением новых element.style.cssText = ''; // Применяем новые стили построчно const styles = css.split(';'); styles.forEach(style => { const [property, value] = style.split(':').map(s => s.trim()); if (property && value) { element.style[property] = value; } }); } catch (e) { MegaLogger.error('Ошибка применения стилей:', e); } } escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } } // ==================== СИСТЕМА ИНТЕРФЕЙСА ==================== class MegaUIManager { constructor() { this.editor = null; this.currentTheme = MEGA_CONFIG.THEMES.DARK; this.isDragging = false; this.dragOffset = { x: 0, y: 0 }; this.glowInterval = null; } createMegaMenuButton() { const menuContainers = [ 'ul[data-toggle-class="menuVisible"]', '.navTabs', '.navigation', 'nav ul', '.menu' ]; let menuContainer = null; for (const selector of menuContainers) { menuContainer = document.querySelector(selector); if (menuContainer) break; } if (!menuContainer) { setTimeout(() => this.createMegaMenuButton(), 1000); return; } if (document.getElementById('mega-uniq-btn')) return; const menuItem = document.createElement('li'); menuItem.innerHTML = ` <a href="#" id="mega-uniq-btn" style="color: #0fa; background: #0fa 100%; color: transparent; -webkit-background-clip: text; text-shadow: 1px 1px 5px #000,0px 0px 5px #000; font-weight: bold;"> MEGA UNIQ v${MEGA_CONFIG.VERSION} </a> `; menuContainer.appendChild(menuItem); menuItem.querySelector('a').addEventListener('click', (e) => { e.preventDefault(); this.showMegaEditor(); }); MegaLogger.success('Мега-кнопка создана'); } showMegaEditor() { if (this.editor) { this.editor.remove(); this.editor = null; } const settings = MegaStorage.getSettings(); this.currentTheme = MEGA_CONFIG.THEMES[settings.theme] || MEGA_CONFIG.THEMES.DARK; this.editor = document.createElement('div'); this.editor.id = 'mega-uniq-editor'; this.editor.innerHTML = this.getMegaEditorHTML(settings); this.applyMegaEditorStyles(); document.body.appendChild(this.editor); this.bindMegaEditorEvents(); this.setupDragAndDrop(); this.applyGlowEffect(); MegaLogger.log('редактор открыт'); } getMegaEditorHTML(settings) { const themeOptions = Object.entries(MEGA_CONFIG.THEMES).map(([key, theme]) => `<option value="${key}" ${settings.theme === key ? 'selected' : ''}>${theme.name}</option>` ).join(''); const glowOptions = Object.entries(MEGA_CONFIG.GLOW_EFFECTS).map(([key, effect]) => `<option value="${key}" ${settings.glowEffect === key ? 'selected' : ''}>${effect.name}</option>` ).join(''); const iconOptions = Object.entries(MEGA_CONFIG.ICONS).map(([key, icon]) => `<option value="${icon}" ${settings.avatarIcon === icon ? 'selected' : ''}>${icon} ${key}</option>` ).join(''); return ` <div class="mega-header"> <div class="mega-title"> MEGA UNIQ v${MEGA_CONFIG.VERSION} <span class="mega-subtitle">ULTIMATE EDITION</span> </div> <div class="mega-controls"> <button class="mega-btn mega-btn-minimize" title="Свернуть">−</button> <button class="mega-btn mega-btn-close" title="Закрыть">×</button> </div> </div> <div class="mega-tabs"> <button class="mega-tab active" data-tab="basic">⚙️ Основные</button> <button class="mega-tab" data-tab="theme">🎨 Тема</button> <button class="mega-tab" data-tab="effects">✨ Эффекты</button> <button class="mega-tab" data-tab="advanced">🔧 Дополнительно</button> <button class="mega-tab" data-tab="stats">📊 Статистика</button> </div> <div class="mega-content"> <!-- ВКЛАДКА ОСНОВНЫЕ --> <div class="mega-tab-content active" data-tab="basic"> <div class="mega-section"> <label>👤 Ваш ID - (Адрес профиля):</label> <input type="text" id="mega-myId" value="${settings.myId}" placeholder="tokyo / 7883978 / "> </div> <div class="mega-section"> <label>👤 Ваш ник:</label> <input type="text" id="mega-myName" value="${settings.myName}" placeholder="Введите ваш текущий ник"> </div> <div class="mega-section"> <label>🆕 Новый ник:</label> <input type="text" id="mega-username" value="${settings.username}" placeholder="Оставьте пустым, если не нужно менять ник"> </div> <div class="mega-section"> <label>🎭 Лычка:</label> <input type="text" id="mega-banner" value="${settings.banner}" placeholder="Текст лычки"> </div> <div class="mega-section"> <label>🎨 CSS для ника:</label> <textarea id="mega-username-css" placeholder="CSS стили для ника">${settings.usernameCss}</textarea> </div> <div class="mega-section"> <label>🎨 CSS для лычки:</label> <textarea id="mega-banner-css" placeholder="CSS стили для лычки">${settings.bannerCss}</textarea> </div> </div> <!-- ВКЛАДКА ТЕМА --> <div class="mega-tab-content" data-tab="theme"> <div class="mega-section"> <label>🎭 Тема оформления:</label> <select id="mega-theme">${themeOptions}</select> </div> <div class="mega-theme-preview"> <div class="mega-preview-card"> <div class="mega-preview-header">Предпросмотр темы</div> <div class="mega-preview-content"> <div class="mega-preview-username">${settings.username || settings.myName}</div> <div class="mega-preview-banner">${settings.banner || 'Лычка'}</div> </div> </div> </div> </div> <!-- ВКЛАДКА ЭФФЕКТЫ --> <div class="mega-tab-content" data-tab="effects"> <div class="mega-section"> <label>✨ Эффект свечения:</label> <select id="mega-glowEffect">${glowOptions}</select> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-enableDrag" ${settings.enableDrag ? 'checked' : ''}> 🖱️ Перетаскивание окна </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-enableAnimations" ${settings.enableAnimations ? 'checked' : ''}> 🎬 Анимации </label> </div> <div class="mega-section"> <button class="mega-btn mega-btn-glow" id="mega-test-glow">Тест свечения</button> </div> </div> <!-- ВКЛАДКА ДОПОЛНИТЕЛЬНО --> <div class="mega-tab-content" data-tab="advanced"> <div class="mega-section"> <label> <input type="checkbox" id="mega-advancedMode" ${settings.advancedMode ? 'checked' : ''}> 🔧 Расширенный режим </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-autoDetect" ${settings.autoDetect ? 'checked' : ''}> 🔍 Авто-определение </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-performanceMode" ${settings.performanceMode ? 'checked' : ''}> ⚡ Режим производительности </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-aggressiveMode" ${settings.aggressiveMode ? 'checked' : ''}> 💥 Агрессивный режим </label> </div> <div class="mega-section"> <button class="mega-btn mega-btn-danger" id="mega-clear-cache">🗑️ Очистить кэш</button> <button class="mega-btn mega-btn-export" id="mega-export">📤 Экспорт</button> <button class="mega-btn mega-btn-import" id="mega-import">📥 Импорт</button> </div> </div> <!-- ВКЛАДКА СТАТИСТИКА --> <div class="mega-tab-content" data-tab="stats"> <div class="mega-stats"> <div class="mega-stat-item"> <span class="mega-stat-label">Версия:</span> <span class="mega-stat-value">v${MEGA_CONFIG.VERSION}</span> </div> <div class="mega-stat-item"> <span class="mega-stat-label">Использований:</span> <span class="mega-stat-value">${settings.usageCount}</span> </div> <div class="mega-stat-item"> <span class="mega-stat-label">Последнее обновление:</span> <span class="mega-stat-value">${settings.lastUpdate ? new Date(settings.lastUpdate).toLocaleString() : 'Никогда'}</span> </div> <div class="mega-stat-item"> <span class="mega-stat-label">Размер хранилища:</span> <span class="mega-stat-value">${MegaStorage.getStorageInfo().size} байт</span> </div> </div> </div> </div> <div class="mega-footer"> <button class="mega-btn mega-btn-primary" id="mega-apply">🚀 Применить</button> <button class="mega-btn mega-btn-secondary" id="mega-save">💾 Сохранить</button> <button class="mega-btn mega-btn-secondary" id="mega-reset">🔄 Сбросить</button> <button class="mega-btn mega-btn-close" id="mega-close">❌ Закрыть</button> </div> `; } applyMegaEditorStyles() { const style = document.createElement('style'); style.textContent = this.getMegaEditorCSS(); document.head.appendChild(style); } getMegaEditorCSS() { return ` #mega-uniq-editor { position: fixed !important; top: 50% !important; left: 18% !important; transform: translate(-50%, -50%) !important; width: 500px !important; min-height: 400px !important; background: ${this.currentTheme.card} !important; border: 1px solid ${this.currentTheme.border} !important; border-radius: 12px !important; z-index: 10000 !important; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; box-shadow: 0 10px 30px rgba(0,0,0,0.3) !important; backdrop-filter: blur(10px) !important; overflow: hidden !important; transition: all 0.3s ease !important; } #mega-uniq-editor:hover { box-shadow: 0 15px 40px rgba(0,0,0,0.4) !important; } .mega-header { background: linear-gradient(135deg, ${this.currentTheme.accent}, ${this.currentTheme.secondary}) !important; color: white !important; padding: 15px 20px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; cursor: move !important; user-select: none !important; } .mega-title { font-size: 18px !important; font-weight: bold !important; } .mega-subtitle { font-size: 10px !important; opacity: 0.8 !important; margin-left: 5px !important; } .mega-controls { display: flex !important; gap: 5px !important; } .mega-btn { background: rgba(255,255,255,0.2) !important; border: none !important; color: white !important; border-radius: 50% !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; font-size: 14px !important; transition: all 0.2s ease !important; } .mega-btn:hover { background: rgba(255,255,255,0.3) !important; transform: scale(1.1) !important; } .mega-tabs { display: flex !important; background: ${this.currentTheme.bg} !important; border-bottom: 1px solid ${this.currentTheme.border} !important; } .mega-tab { flex: 1 !important; background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05)) !important; border: none !important; border-right: 1px solid ${this.currentTheme.border} !important; padding: 15px 8px !important; color: ${this.currentTheme.text} !important; cursor: pointer !important; transition: all 0.3s ease !important; font-size: 11px !important; font-weight: 600 !important; letter-spacing: 0.3px !important; text-transform: uppercase !important; position: relative !important; overflow: hidden !important; } .mega-tab:last-child { border-right: none !important; } .mega-tab:hover { background: linear-gradient(135deg, rgba(255,255,255,0.15), rgba(255,255,255,0.1)) !important; transform: translateY(-1px) !important; } .mega-tab.active { background: linear-gradient(135deg, ${this.currentTheme.accent}, ${this.currentTheme.secondary}) !important; color: white !important; box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important; } .mega-tab.active::before { content: '' !important; position: absolute !important; bottom: 0 !important; left: 50% !important; transform: translateX(-50%) !important; width: 30px !important; height: 3px !important; background: white !important; border-radius: 2px !important; } .mega-tab:hover { background: rgba(255,255,255,0.1) !important; } .mega-tab.active { background: ${this.currentTheme.accent} !important; color: white !important; } .mega-content { padding: 20px !important; background: ${this.currentTheme.card} !important; max-height: 400px !important; overflow-y: auto !important; } .mega-tab-content { display: none !important; } .mega-tab-content.active { display: block !important; } .mega-section { margin-bottom: 15px !important; } .mega-section label { display: block !important; color: ${this.currentTheme.text} !important; margin-bottom: 5px !important; font-weight: 500 !important; } .mega-section input[type="text"], .mega-section textarea, .mega-section select { width: 95% !important; padding: 10px !important; border: 1px solid ${this.currentTheme.border} !important; border-radius: 6px !important; background: ${this.currentTheme.bg} !important; color: ${this.currentTheme.text} !important; font-size: 14px !important; transition: all 0.3s ease !important; } .mega-section input[type="text"]:focus, .mega-section textarea:focus, .mega-section select:focus { outline: none !important; border-color: ${this.currentTheme.accent} !important; box-shadow: 0 0 0 2px rgba(255,107,107,0.2) !important; } .mega-section textarea { min-height: 60px !important; resize: vertical !important; } .mega-theme-preview { margin-top: 20px !important; } .mega-preview-card { background: ${this.currentTheme.bg} !important; border: 1px solid ${this.currentTheme.border} !important; border-radius: 8px !important; padding: 15px !important; } .mega-preview-header { color: ${this.currentTheme.accent} !important; font-weight: bold !important; margin-bottom: 10px !important; } .mega-preview-username { color: ${this.currentTheme.text} !important; margin-bottom: 5px !important; } .mega-preview-banner { background: ${this.currentTheme.accent} !important; color: white !important; padding: 2px 8px !important; border-radius: 3px !important; display: inline-block !important; } .mega-stats { display: grid !important; gap: 10px !important; } .mega-stat-item { display: flex !important; justify-content: space-between !important; align-items: center !important; padding: 8px 0 !important; border-bottom: 1px solid ${this.currentTheme.border} !important; } .mega-stat-label { color: ${this.currentTheme.text} !important; opacity: 0.8 !important; } .mega-stat-value { color: ${this.currentTheme.accent} !important; font-weight: bold !important; } .mega-footer { padding: 15px 20px !important; background: ${this.currentTheme.bg} !important; border-top: 1px solid ${this.currentTheme.border} !important; display: flex !important; gap: 10px !important; justify-content: flex-end !important; } .mega-btn-primary { background: linear-gradient(135deg, ${this.currentTheme.accent}, ${this.currentTheme.secondary}) !important; color: white !important; border: none !important; padding: 12px 24px !important; border-radius: 8px !important; cursor: pointer !important; font-weight: 600 !important; transition: all 0.3s ease !important; position: relative !important; overflow: hidden !important; box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important; letter-spacing: 0.5px !important; } .mega-btn-primary:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(0,0,0,0.3) !important; } .mega-btn-primary:active { transform: translateY(0) !important; } .mega-btn-primary::before { content: '' !important; position: absolute !important; top: 0 !important; left: -100% !important; width: 100% !important; height: 100% !important; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent) !important; transition: left 0.5s ease !important; } .mega-btn-primary:hover::before { left: 100% !important; } .mega-btn-secondary { background: linear-gradient(135deg, ${this.currentTheme.border}, rgba(255,255,255,0.1)) !important; color: ${this.currentTheme.text} !important; border: 1px solid ${this.currentTheme.border} !important; padding: 11px 20px !important; border-radius: 8px !important; cursor: pointer !important; transition: all 0.3s ease !important; font-weight: 500 !important; position: relative !important; overflow: hidden !important; } .mega-btn-secondary:hover { background: linear-gradient(135deg, ${this.currentTheme.accent}, ${this.currentTheme.secondary}) !important; color: white !important; border-color: transparent !important; transform: translateY(-1px) !important; box-shadow: 0 4px 15px rgba(0,0,0,0.2) !important; } /* Пульсация для активных кнопок */ @keyframes buttonPulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .mega-btn-primary:focus { animation: buttonPulse 0.6s ease-in-out !important; } /* Эффект нажатия */ .mega-btn:active { transform: scale(0.95) !important; transition: transform 0.1s ease !important; } /* Градиентные границы для премиум вида */ .mega-tab-content .mega-section { position: relative !important; } .mega-tab-content .mega-section::before { content: '' !important; position: absolute !important; top: 0 !important; left: 0 !important; right: 0 !important; height: 1px !important; background: linear-gradient(90deg, transparent, ${this.currentTheme.accent}, transparent) !important; } .mega-tab { font-family: 'Segoe UI', 'Arial', sans-serif !important; text-shadow: 0 1px 2px rgba(0,0,0,0.3) !important; } .mega-btn-primary, .mega-btn-secondary { font-family: 'Segoe UI', 'Arial', sans-serif !important; text-shadow: 0 1px 2px rgba(0,0,0,0.3) !important; } `; } bindMegaEditorEvents() { // Закрытие редактора this.editor.querySelector('.mega-btn-close').addEventListener('click', () => this.closeEditor()); this.editor.querySelector('#mega-close').addEventListener('click', () => this.closeEditor()); // Сворачивание this.editor.querySelector('.mega-btn-minimize').addEventListener('click', () => this.minimizeEditor()); // Переключение вкладок this.editor.querySelectorAll('.mega-tab').forEach(tab => { tab.addEventListener('click', () => this.switchTab(tab.dataset.tab)); }); // Применение настроек this.editor.querySelector('#mega-apply').addEventListener('click', () => this.applySettings()); this.editor.querySelector('#mega-save').addEventListener('click', () => this.saveSettings()); this.editor.querySelector('#mega-reset').addEventListener('click', () => this.resetSettings()); // Обработка изменений темы this.editor.querySelector('#mega-theme').addEventListener('change', (e) => this.changeTheme(e.target.value)); // Тест свечения this.editor.querySelector('#mega-test-glow').addEventListener('click', () => this.testGlowEffect()); // Дополнительные кнопки this.editor.querySelector('#mega-clear-cache').addEventListener('click', () => this.clearCache()); this.editor.querySelector('#mega-export').addEventListener('click', () => this.exportSettings()); this.editor.querySelector('#mega-import').addEventListener('click', () => this.importSettings()); // Обработка изменений в реальном времени this.setupLivePreview(); } setupDragAndDrop() { const editor = this.editor; const header = editor.querySelector('.mega-header'); const settings = MegaStorage.getSettings(); if (!settings.enableDrag) return; let isDragging = false; let dragOffset = { x: 0, y: 0 }; const startDrag = (e) => { if (e.target.closest('.mega-btn')) return; isDragging = true; const rect = editor.getBoundingClientRect(); dragOffset.x = e.clientX - rect.left; dragOffset.y = e.clientY - rect.top; editor.classList.add('mega-dragging'); document.body.style.userSelect = 'none'; document.body.style.cursor = 'grabbing'; }; const doDrag = (e) => { if (!isDragging) return; const x = e.clientX - dragOffset.x; const y = e.clientY - dragOffset.y; // Разрешаем перемещение за пределы окна с ограничениями const maxX = window.innerWidth - editor.offsetWidth; const maxY = window.innerHeight - editor.offsetHeight; editor.style.left = Math.max(0, Math.min(x, maxX)) + 'px'; editor.style.top = Math.max(0, Math.min(y, maxY)) + 'px'; editor.style.transform = 'none'; // Убираем трансформ при перетаскивании }; const stopDrag = () => { if (isDragging) { isDragging = false; editor.classList.remove('mega-dragging'); document.body.style.userSelect = ''; document.body.style.cursor = ''; // Сохраняем позицию MegaStorage.saveWindowPosition({ x: parseInt(editor.style.left), y: parseInt(editor.style.top) }); } }; // Обработчики для мыши header.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', doDrag); document.addEventListener('mouseup', stopDrag); // Обработчики для тач-устройств header.addEventListener('touchstart', (e) => { e.preventDefault(); startDrag(e.touches[0]); }); document.addEventListener('touchmove', (e) => { e.preventDefault(); doDrag(e.touches[0]); }); document.addEventListener('touchend', stopDrag); // Восстанавливаем сохраненную позицию с проверкой на видимость const savedPos = settings.windowPosition; if (savedPos && savedPos.x !== undefined && savedPos.y !== undefined) { // Проверяем, что позиция в пределах видимой области const maxX = window.innerWidth - editor.offsetWidth; const maxY = window.innerHeight - editor.offsetHeight; if (savedPos.x >= 0 && savedPos.x <= maxX && savedPos.y >= 0 && savedPos.y <= maxY) { editor.style.left = savedPos.x + 'px'; editor.style.top = savedPos.y + 'px'; editor.style.transform = 'none'; } else { // Если позиция за пределами экрана - центрируем editor.style.left = '15%'; editor.style.top = '50%'; editor.style.transform = 'translate(-50%, -50%)'; } } else { // Позиция по умолчанию - центр экрана editor.style.left = '15%'; editor.style.top = '50%'; editor.style.transform = 'translate(-50%, -50%)'; } } applyGlowEffect() { const settings = MegaStorage.getSettings(); const editor = this.editor; // Очищаем предыдущие эффекты if (this.glowInterval) { clearInterval(this.glowInterval); editor.classList.remove('mega-glow-effect'); } const glowEffect = MEGA_CONFIG.GLOW_EFFECTS[settings.glowEffect]; if (!glowEffect || glowEffect.value === 'none') return; let glowValue = glowEffect.value; if (glowValue === 'current') { glowValue = this.currentTheme.glow; } if (settings.glowEffect === 'PULSATING' && settings.enableAnimations) { editor.classList.add('mega-glow-effect'); } else { editor.style.boxShadow = glowValue; } } switchTab(tabName) { // Скрываем все вкладки this.editor.querySelectorAll('.mega-tab-content').forEach(content => { content.classList.remove('active'); }); // Убираем активность у всех кнопок this.editor.querySelectorAll('.mega-tab').forEach(tab => { tab.classList.remove('active'); }); // Показываем выбранную вкладку this.editor.querySelector(`.mega-tab-content[data-tab="${tabName}"]`).classList.add('active'); this.editor.querySelector(`.mega-tab[data-tab="${tabName}"]`).classList.add('active'); } changeTheme(themeKey) { this.currentTheme = MEGA_CONFIG.THEMES[themeKey] || MEGA_CONFIG.THEMES.DARK; this.applyMegaEditorStyles(); this.applyGlowEffect(); } testGlowEffect() { const editor = this.editor; editor.style.transition = 'box-shadow 0.5s ease'; const originalGlow = editor.style.boxShadow; editor.style.boxShadow = '0 0 50px ' + this.currentTheme.accent; setTimeout(() => { editor.style.boxShadow = originalGlow; setTimeout(() => { editor.style.boxShadow = '0 0 70px ' + this.currentTheme.secondary; setTimeout(() => { editor.style.boxShadow = originalGlow; }, 500); }, 500); }, 500); } setupLivePreview() { // Обновление предпросмотра темы this.editor.querySelector('#mega-theme').addEventListener('change', (e) => { this.updateThemePreview(); }); this.editor.querySelector('#mega-username').addEventListener('input', (e) => { this.updateThemePreview(); }); this.editor.querySelector('#mega-banner').addEventListener('input', (e) => { this.updateThemePreview(); }); } updateThemePreview() { const theme = MEGA_CONFIG.THEMES[this.editor.querySelector('#mega-theme').value] || MEGA_CONFIG.THEMES.DARK; const username = this.editor.querySelector('#mega-username').value || 'Имя пользователя'; const banner = this.editor.querySelector('#mega-banner').value || 'Лычка'; const previewCard = this.editor.querySelector('.mega-preview-card'); if (previewCard) { previewCard.style.background = theme.bg; previewCard.style.borderColor = theme.border; const header = previewCard.querySelector('.mega-preview-header'); if (header) header.style.color = theme.accent; const usernameElem = previewCard.querySelector('.mega-preview-username'); if (usernameElem) { usernameElem.textContent = username; usernameElem.style.color = theme.text; } const bannerElem = previewCard.querySelector('.mega-preview-banner'); if (bannerElem) { bannerElem.textContent = banner; bannerElem.style.background = theme.accent; } } } getCurrentSettings() { const editor = this.editor; return { myId: editor.querySelector('#mega-myId').value, myName: editor.querySelector('#mega-myName').value, username: editor.querySelector('#mega-username').value, banner: editor.querySelector('#mega-banner').value, usernameCss: editor.querySelector('#mega-username-css').value, bannerCss: editor.querySelector('#mega-banner-css').value, avatarIcon: editor.querySelector('#mega-avatarIcon') ? editor.querySelector('#mega-avatarIcon').value : '', bannerIcon: editor.querySelector('#mega-bannerIcon') ? editor.querySelector('#mega-bannerIcon').value : '', usernameIcon: editor.querySelector('#mega-usernameIcon') ? editor.querySelector('#mega-usernameIcon').value : '', theme: editor.querySelector('#mega-theme').value, glowEffect: editor.querySelector('#mega-glowEffect').value, enableDrag: editor.querySelector('#mega-enableDrag').checked, enableAnimations: editor.querySelector('#mega-enableAnimations').checked, advancedMode: editor.querySelector('#mega-advancedMode').checked, autoDetect: editor.querySelector('#mega-autoDetect').checked, performanceMode: editor.querySelector('#mega-performanceMode').checked, aggressiveMode: editor.querySelector('#mega-aggressiveMode').checked, usageCount: MegaStorage.getSettings().usageCount, lastUpdate: new Date().toISOString() }; } applySettings() { const settings = this.getCurrentSettings(); if (MegaStorage.saveSettings(settings)) { const processor = new MegaDOMProcessor(settings); processor.applyMegaUniq(); this.showNotification('Настройки применены!', 'success'); } else { this.showNotification('Ошибка сохранения настроек!', 'error'); } } saveSettings() { const settings = this.getCurrentSettings(); if (MegaStorage.saveSettings(settings)) { this.showNotification('Настройки сохранены!', 'success'); } else { this.showNotification('Ошибка сохранения настроек!', 'error'); } } resetSettings() { if (confirm('Вы уверены, что хотите сбросить все настройки?')) { const defaultSettings = MegaStorage.getDefaultSettings(); MegaStorage.saveSettings(defaultSettings); this.closeEditor(); this.showMegaEditor(); this.showNotification('Настройки сброшены!', 'success'); } } clearCache() { if (confirm('Очистить весь кэш и настройки?')) { const cleared = MegaStorage.clearCache(); this.showNotification(`Очищено ${cleared} записей!`, 'success'); setTimeout(() => location.reload(), 100); } } exportSettings() { const settings = MegaStorage.exportSettings(); const blob = new Blob([settings], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `mega-uniq-settings-${new Date().toISOString().split('T')[0]}.json`; a.click(); URL.revokeObjectURL(url); this.showNotification('Настройки экспортированы!', 'success'); } importSettings() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { if (MegaStorage.importSettings(event.target.result)) { this.closeEditor(); this.showMegaEditor(); this.showNotification('Настройки импортированы!', 'success'); } else { this.showNotification('Ошибка импорта настроек!', 'error'); } }; reader.readAsText(file); } }; input.click(); } minimizeEditor() { this.editor.style.transform = 'scale(0.8)'; this.editor.style.opacity = '0.5'; setTimeout(() => { this.editor.style.display = 'none'; }, 100); // TODO: Добавить кнопку восстановления } closeEditor() { if (this.editor) { // Очищаем обработчики перетаскивания if (this.cleanupDragHandlers) { this.cleanupDragHandlers(); } this.editor.style.transform = 'scale(0.8)'; this.editor.style.opacity = '0'; setTimeout(() => { this.editor.remove(); this.editor = null; }, 300); } } showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 15px 20px; border-radius: 8px; color: white; font-weight: bold; z-index: 10001; transition: all 0.3s ease; background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#f44336' : '#2196f3'}; `; document.body.appendChild(notification); setTimeout(() => { notification.style.transform = 'translateX(100%)'; setTimeout(() => notification.remove(), 300); }, 100); } } // ==================== ИНИЦИАЛИЗАЦИЯ ==================== class MegaUniqApp { constructor() { this.uiManager = new MegaUIManager(); this.processor = null; this.settings = null; this.isInitialized = false; this.observer = null; } async init() { try { MegaLogger.log('🚀 Инициализация MEGA UNIQ...'); // Загружаем настройки this.settings = MegaStorage.getSettings(); MegaLogger.log('Настройки загружены:', this.settings); // Создаем кнопку меню this.waitForMenu().then(() => { this.uiManager.createMegaMenuButton(); }); // Инициализируем процессор this.processor = new MegaDOMProcessor(this.settings); // Начинаем обработку this.startProcessing(); // Настраиваем наблюдатель за изменениями DOM this.setupObserver(); this.isInitialized = true; MegaLogger.success('MEGA UNIQ успешно инициализирован!'); } catch (error) { MegaLogger.error('Критическая ошибка инициализации:', error); } } async waitForMenu() { return new Promise((resolve) => { const checkMenu = () => { const menuContainers = [ 'ul[data-toggle-class="menuVisible"]', '.navTabs', '.navigation', 'nav ul', '.menu' ]; for (const selector of menuContainers) { if (document.querySelector(selector)) { resolve(); return; } } setTimeout(checkMenu, 100); }; checkMenu(); }); } startProcessing() { // Первоначальная обработка setTimeout(() => { if (this.settings.myId && this.settings.myName) { this.processor.applyMegaUniq(); } }, 100); // Периодическая обработка setInterval(() => { if (this.settings.autoDetect && this.settings.myId && this.settings.myName) { this.processor.applyMegaUniq(); } }, MEGA_CONFIG.UPDATE_INTERVAL); } setupObserver() { if (!this.settings.autoDetect) return; this.observer = new MutationObserver((mutations) => { let shouldProcess = false; mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { const element = node; if (element.querySelector && ( element.querySelector(`a[href*="/members/${this.settings.myId}/"]`) || element.querySelector('.userBanner') || element.querySelector('.avatar') )) { shouldProcess = true; } } }); } }); if (shouldProcess) { setTimeout(() => { this.processor.applyMegaUniq(); }, MEGA_CONFIG.OBSERVER_DEBOUNCE); } }); this.observer.observe(document.body, { childList: true, subtree: true }); } destroy() { if (this.observer) { this.observer.disconnect(); } MegaLogger.log('MEGA UNIQ уничтожен'); } } // ==================== ЗАПУСК ПРИЛОЖЕНИЯ ==================== let megaApp; function initializeApp() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { megaApp = new MegaUniqApp(); megaApp.init(); }); } else { megaApp = new MegaUniqApp(); megaApp.init(); } } // Защита от множественной инициализации if (!window.megaUniqInitialized) { window.megaUniqInitialized = true; initializeApp(); } })();