您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Some useful utilities for Lolzteam
当前为
// ==UserScript== // @name LZT Local Uniq // @version 1.0.8 // @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"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>Основные</button> <button class="mega-tab" data-tab="theme"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"></rect><line x1="7" y1="2" x2="7" y2="22"></line><line x1="17" y1="2" x2="17" y2="22"></line><line x1="2" y1="12" x2="22" y2="12"></line><line x1="2" y1="7" x2="7" y2="7"></line><line x1="2" y1="17" x2="7" y2="17"></line><line x1="17" y1="17" x2="22" y2="17"></line><line x1="17" y1="7" x2="22" y2="7"></line></svg>Тема</button> <button class="mega-tab" data-tab="effects"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>Эффекты</button> <button class="mega-tab" data-tab="advanced"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><line x1="4" y1="21" x2="4" y2="14"></line><line x1="4" y1="10" x2="4" y2="3"></line><line x1="12" y1="21" x2="12" y2="12"></line><line x1="12" y1="8" x2="12" y2="3"></line><line x1="20" y1="21" x2="20" y2="16"></line><line x1="20" y1="12" x2="20" y2="3"></line><line x1="1" y1="14" x2="7" y2="14"></line><line x1="9" y1="8" x2="15" y2="8"></line><line x1="17" y1="16" x2="23" y2="16"></line></svg>Дополнительно</button> <button class="mega-tab" data-tab="stats"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><line x1="18" y1="20" x2="18" y2="10"></line><line x1="12" y1="20" x2="12" y2="4"></line><line x1="6" y1="20" x2="6" y2="14"></line></svg>Статистика</button> </div> <div class="mega-content"> <!-- ВКЛАДКА ОСНОВНЫЕ --> <div class="mega-tab-content active" data-tab="basic"> <div class="mega-section"> <label> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path> <circle cx="12" cy="7" r="4"></circle> </svg> Ваш ID - (Адрес профиля): </label> <input type="text" id="mega-myId" value="${settings.myId}" placeholder="tokyo / 7883978 / "> </div> <div class="mega-section"> <label> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"> <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path> <circle cx="12" cy="7" r="4"></circle> </svg> Ваш ник: </label> <input type="text" id="mega-myName" value="${settings.myName}" placeholder="Введите ваш текущий ник"> </div> <div class="mega-section"> <label> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"> <circle cx="12" cy="12" r="10"></circle> <line x1="12" y1="8" x2="12" y2="16"></line> <line x1="8" y1="12" x2="16" y2="12"></line> </svg> Новый ник: </label> <input type="text" id="mega-username" value="${settings.username}" placeholder="Оставьте пустым, если не нужно менять ник"> </div> <div class="mega-section"> <label> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"> <line x1="22" y1="2" x2="11" y2="13"></line> <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon> </svg> Лычка: </label> <input type="text" id="mega-banner" value="${settings.banner}" placeholder="Текст лычки"> </div> <div class="mega-section"> <label> <img src="data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath opacity='0.12' d='M2 12C2 17.5228 6.47715 22 12 22C13.6569 22 15 20.6569 15 19V18.5C15 18.0356 15 17.8034 15.0257 17.6084C15.2029 16.2622 16.2622 15.2029 17.6084 15.0257C17.8034 15 18.0356 15 18.5 15H19C20.6569 15 22 13.6569 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z' fill='%23505050'/%3E%3Cpath d='M2 12C2 17.5228 6.47715 22 12 22C13.6569 22 15 20.6569 15 19V18.5C15 18.0356 15 17.8034 15.0257 17.6084C15.2029 16.2622 16.2622 15.2029 17.6084 15.0257C17.8034 15 18.0356 15 18.5 15H19C20.6569 15 22 13.6569 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M7 13C7.55228 13 8 12.5523 8 12C8 11.4477 7.55228 11 7 11C6.44772 11 6 11.4477 6 12C6 12.5523 6.44772 13 7 13Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M16 9C16.5523 9 17 8.55228 17 8C17 7.44772 16.5523 7 16 7C15.4477 7 15 7.44772 15 8C15 8.55228 15.4477 9 16 9Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10 8C10.5523 8 11 7.55228 11 7C11 6.44772 10.5523 6 10 6C9.44772 6 9 6.44772 9 7C9 7.55228 9.44772 8 10 8Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A" alt="CSS" style="width: 16px; height: 16px; margin-right: 5px; vertical-align: middle;"> CSS для ника: </label> <textarea id="mega-username-css" placeholder="CSS стили для ника">${settings.usernameCss}</textarea> </div> <div class="mega-section"> <label> <img src="data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath opacity='0.12' d='M2 12C2 17.5228 6.47715 22 12 22C13.6569 22 15 20.6569 15 19V18.5C15 18.0356 15 17.8034 15.0257 17.6084C15.2029 16.2622 16.2622 15.2029 17.6084 15.0257C17.8034 15 18.0356 15 18.5 15H19C20.6569 15 22 13.6569 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z' fill='%23505050'/%3E%3Cpath d='M2 12C2 17.5228 6.47715 22 12 22C13.6569 22 15 20.6569 15 19V18.5C15 18.0356 15 17.8034 15.0257 17.6084C15.2029 16.2622 16.2622 15.2029 17.6084 15.0257C17.8034 15 18.0356 15 18.5 15H19C20.6569 15 22 13.6569 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M7 13C7.55228 13 8 12.5523 8 12C8 11.4477 7.55228 11 7 11C6.44772 11 6 11.4477 6 12C6 12.5523 6.44772 13 7 13Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M16 9C16.5523 9 17 8.55228 17 8C17 7.44772 16.5523 7 16 7C15.4477 7 15 7.44772 15 8C15 8.55228 15.4477 9 16 9Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M10 8C10.5523 8 11 7.55228 11 7C11 6.44772 10.5523 6 10 6C9.44772 6 9 6.44772 9 7C9 7.55228 9.44772 8 10 8Z' stroke='%23505050' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A" alt="CSS" style="width: 16px; height: 16px; margin-right: 5px; vertical-align: middle;"> 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><i class="fal fa-photo-video" aria-hidden="true" style="margin-right: 5px;"></i>Тема оформления: </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><i class="fa fa-magic" aria-hidden="true" style="margin-right: 5px;"></i>Эффект свечения:</label> <select id="mega-glowEffect">${glowOptions}</select> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-enableDrag" ${settings.enableDrag ? 'checked' : ''}><i class="fa fa-arrows" aria-hidden="true" style="margin-right: 5px;"></i>Перетаскивание окна </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-enableAnimations" ${settings.enableAnimations ? 'checked' : ''}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line></svg>Анимации </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' : ''}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>Расширенный режим </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-autoDetect" ${settings.autoDetect ? 'checked' : ''}> <div style="display: inline-block; margin-right: 5px; vertical-align: middle; position: relative;"> <i class="QuickSearchIcon far fa-search" style="color: currentColor; font-size: 14px;"></i> </div> Авто-определение </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-performanceMode" ${settings.performanceMode ? 'checked' : ''}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>Режим производительности </label> </div> <div class="mega-section"> <label> <input type="checkbox" id="mega-aggressiveMode" ${settings.aggressiveMode ? 'checked' : ''}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 5px; vertical-align: middle;"><path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"></path></svg>Агрессивный режим </label> </div> <div class="mega-section"> <button class="mega-btn mega-btn-danger" id="mega-clear-cache"><i class="iconKey far fa-trash-alt"></i> Очистить кэш</button> <button class="mega-btn mega-btn-export" id="mega-export"><i class="fa fa-cloud-upload" aria-hidden="true" style="margin-right: 5px;"></i>Экспорт</button> <button class="mega-btn mega-btn-import" id="mega-import"><i class="fa fa-cloud-download" aria-hidden="true" style="margin-right: 5px;"></i>Импорт</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"> <i class="fa fa-check" aria-hidden="true" style="margin-right: 5px;"></i>Применить</button> <button class="mega-btn mega-btn-secondary" id="mega-save" style="display: flex; flex-direction: column; align-items: center; padding: 8px 12px; gap: 2px;"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><path d="M17 21v-8H7v8"></path><path d="M7 3v5h8"></path></svg><span style="font-size: 11px;">Сохранить</span></button> <button class="mega-btn mega-btn-secondary" id="mega-reset"><span class="svgIconLink" style="display: inline-block; margin-right: 5px; vertical-align: middle;"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M22 10C22 10 19.995 7.26822 18.3662 5.63824C16.7373 4.00827 14.4864 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.1031 21 19.5649 18.2543 20.6482 14.5M22 10V4M22 10H16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span>Сбросить</button> <button class="mega-btn mega-btn-close" id="mega-close" style="padding: 12px; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></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(); } })();