// ==UserScript== // @name 4PDA Logo // @namespace http://4pda.to/forum/index.php // @version 4.3 // @description Замена логотипа в посте // @author BrantX // @match http*://4pda.to/forum/* // @grant none // @run-at document-end // @downloadURL none // ==/UserScript== (function () { 'use strict'; console.log('Logo Replacer v4.3 LOADED'); // База логотипов const LOGOS = [ { url: 'https://4pda.to/s/HL5V5qDA1xLMwNxwm561PwRaFF6TOoR9wwZ.png', description: 'AICP' }, { url: 'https://4pda.to/s/HL5V1GHLxxp1z1TgHPZRKOvz0Rlz2z1H61bgCj7.png', description: 'AICP_2' }, { url: 'https://4pda.to/s/HL5V6p1Ko5MmbdZ6sAUz0z25RaFFcDmsHOneE.png', description: 'AlphaDroid_2' }, { url: 'https://4pda.to/s/HL5V7m7RgvmFbd3skM8Q33KypfPDmsnefKe.png', description: 'AlphaDroid' }, { url: 'https://4pda.to/s/HL5VEdX4wHqbaab9ksu2VLJGz0IJyBy0ppDt.png', description: 'Ancient' }, { url: 'https://4pda.to/s/HL5VFadBYjIQaa5vsgkbZJS81qiyByW3hnH.png', description: 'Android Beta' }, { url: 'https://4pda.to/s/HL5V0xNpkJnip0z2Gn6Wz1PwxKtZ86YRSbq19.png', description: 'Android' }, { url: 'https://4pda.to/s/HL5V1uHyslNJp0VWfQsPbyqCB5t6YRyLiz0l.png', description: 'ApolloOS' }, { url: 'https://4pda.to/s/HL5V2z2TY5HKrim7SlLkb33qCB5NMAVs4dl2.png', description: 'AwakenOS' }, { url: 'https://4pda.to/s/HL5V3yRjTjoAimdit9u2z25xKtZeMAVMqz2Ja.png', description: 'AxionOS' }, { url: 'https://4pda.to/s/HL5V4p3HxIogyO3sEcmsj8cX6O2N9PvSRfj.png', description: 'BaikalOS' }, { url: 'https://4pda.to/s/HL5V5m5UZkKLyOZ6MwcHHEfvwz1z0N9PPi3LB.png', description: 'BananaDroid' }, { url: 'https://4pda.to/s/HL5V6t90GGNpZexwGrz1jtnfvwz1T7XTJz087c.png', description: 'Bliss ROM' }, { url: 'https://4pda.to/s/HL5V7qFF8inCZeRA8feABtcX6OY7XTpDGx0.png', description: 'Bootleggers' }, { url: 'https://4pda.to/s/HL5VEhvuSkq5qinhxtRKu95tXa4uXz0Z54PL.png', description: 'CalyxOS_1' }, { url: 'https://4pda.to/s/HL5VFez2t4IIwqiHRZhDp4FAlT2xuXz03rSbp.png', description: 'CalyxOS_2' }, { url: 'https://4pda.to/s/HL5V0tFF8inCZ8hoa73ez1cjphLV28Qz2J3Lh.png', description: 'Cherish OS' }, { url: 'https://4pda.to/s/HL5V1q90GGNpZ8B2yRLF2WYhNpW28QVZRfD.png', description: 'CipherOS' }, { url: 'https://4pda.to/s/HL5V2p5UZkKLyuJz1wKDpaVYhNp0IWULoGxW.png', description: 'ColtOS' }, { url: 'https://4pda.to/s/HL5V3m3HxIogyupEY8RKOPjphLz2IWUr2876.png', description: 'crDroid' }, { url: 'https://4pda.to/s/HL5V4z2RjTjoAiGNKRdJWAKm6QkLJZOQgiz0F.png', description: 'DerpFest' }, { url: 'https://4pda.to/s/HL5V5yTY5HKriGta3x57sIz2Uc8gJZOwQq1f.png', description: 'DivestOS' }, { url: 'https://4pda.to/s/HL5V6xHyslNJpWlO5qTxGjz2Uc8A3BSmBz2J4.png', description: 'Evolution X' }, { url: 'https://4pda.to/s/HL5V7uNpkJnipWFeTeBSihm6Qkr3BSGxdlY.png', description: 'GrapheneOS' }, { url: 'https://4pda.to/s/HL5VElniz1xr6oZfNT8x4mz0tgKLz2omMXWz0sz0.png', description: 'HentaiOS' }, { url: 'https://4pda.to/s/HL5VFitZc7JvoZ9d5KjZCxuoep0omM1GbAR.png', description: 'HorizonDroid' }, { url: 'https://4pda.to/s/HL5V0p7RgvmFb7pE2uZusIVkUaa8Pnz0sww3.png', description: 'Infinity' }, { url: 'https://4pda.to/s/HL5V1m1Ko5Mmb7Jz1QarVAKGsY2R8PnT6Y6b.png', description: 'iode' }, { url: 'https://4pda.to/s/HL5V2tDA1xLMwtB2ShjZihGsY2xOnrNNfK8.png', description: 'Kirisakura' }, { url: 'https://4pda.to/s/HL5V3qB5P7pfwtho4tx4GjVkUa4Onrtdnek.png', description: 'LineageOS' }, { url: 'https://4pda.to/s/HL5V4xJvz2up9gVFez0Opm2W2RlVkPopOFLId.png', description: 'LMODroid' }, { url: 'https://4pda.to/s/HL5V5uLsd4LsgVlOb4bNz1cD3JvHPopuz2Dk1.png', description: 'Martixx' }, { url: 'https://4pda.to/s/HL5V6z2PeKwMGrltaZBz0hOPD3Jvn9Qtok6yi.png', description: 'MistOS' }, { url: 'https://4pda.to/s/HL5V7yVdC6mlrlNKxNhCaV2RlVE9QtIUU0A.png', description: 'Paranoid' }, { url: 'https://4pda.to/s/HL5VEBfuSkq5qinhxtRKu95tXa4uXz0Z54PL.png', description: 'PhoenixAOSP' }, { url: 'https://4pda.to/s/HL5VF8lt4IIwqiHRZhDp4FAlT2xuXz03rSbp.png', description: 'PixelDust' }, { url: 'https://4pda.to/s/HL5V0NVF8inCZ8hoa73ez1cjphLV28Qz2J3Lh.png', description: 'PixelExperience' }, { url: 'https://4pda.to/s/HL5V1KP0GGNpZ8B2yRLF2WYhNpW28QVZRfD.png', description: 'Pixelos' }, { url: 'https://4pda.to/s/HL5V2JLUZkKLyuJz1wKDpaVYhNp0IWULoGxW.png', description: 'PixelPlus UI' }, { url: 'https://4pda.to/s/HL5V3GJHxIogyupEY8RKOPjphLz2IWUr2876.png', description: 'Project Blaze' }, { url: 'https://4pda.to/s/HL5V4VBjTjoAiGNKRdJWAKm6QkLJZOQgiz0F.png', description: 'Project elixir-new' }, { url: 'https://4pda.to/s/HL5V5SDY5HKriGta3x57sIz2Uc8gJZOwQq1f.png', description: 'Project Elixir' }, { url: 'https://4pda.to/s/HL5V6R1yslNJpWlO5qTxGjz2Uc8A3BSmBz2J4.png', description: 'Project Zephyrus' }, { url: 'https://4pda.to/s/HL5V7O7pkJnipWFeTeBSihm6Qkr3BSGxdlY.png', description: 'ProjectEverest' }, { url: 'https://4pda.to/s/HL5VEFXiz1xr6oZfNT8x4mz0tgKLz2omMXWz0sz0.png', description: 'Radioactive Kernel' }, { url: 'https://4pda.to/s/HL5VFCdZc7JvoZ9d5KjZCxuoep0omM1GbAR.png', description: 'RisingOS' }, { url: 'https://4pda.to/s/HL5V0JNRgvmFb7pE2uZusIVkUaa8Pnz0sww3.png', description: 'Sigmadroid_2' }, { url: 'https://4pda.to/s/HL5V1GHKo5Mmb7Jz1QarVAKGsY2R8PnT6Y6b.png', description: 'Sigmadroid' }, { url: 'https://4pda.to/s/HL5V2NTA1xLMwtB2ShjZihGsY2xOnrNNfK8.png', description: 'SparkOS' }, { url: 'https://4pda.to/s/HL5V3KR5P7pfwtho4tx4GjVkUa4Onrtdnek.png', description: 'StagOS' }, { url: 'https://4pda.to/s/HL5V4R3vz2up9gVFez0Opm2W2RlVkPopOFLId.png', description: 'StatixOS' }, { url: 'https://4pda.to/s/HL5V5O5sd4LsgVlOb4bNz1cD3JvHPopuz2Dk1.png', description: 'Superior_2' }, { url: 'https://4pda.to/s/HL5V6V9eKwMGrltaZBz0hOPD3Jvn9Qtok6yi.png', description: 'Superior' }, { url: 'https://4pda.to/s/HL5V7SFdC6mlrlNKxNhCaV2RlVE9QtIUU0A.png', description: 'SyberiaOS' } ]; let buttonAdded = false; let logoPanel = null; function addLogoButton() { if (buttonAdded) return; console.log('Logo Replacer: searching for editor...'); const selectors = [ 'img[src*="folder_editor_buttons"]', 'td.formbuttons img', '.buttons img', 'input[value*="B"]', 'input[value*="I"]', 'input[value*="U"]', 'textarea[name="Post"]' ]; for (let selector of selectors) { const elements = document.querySelectorAll(selector); if (elements.length > 0) { const element = elements[0]; addButtonAsFirst(element); return; } } } function addButtonAsFirst(element) { if (!element || buttonAdded) return; console.log('Logo Replacer: adding button as first element'); const logoBtn = document.createElement('span'); logoBtn.className = 'g-btn blue logo-replacer-btn'; logoBtn.textContent = 'Лого'; logoBtn.title = `Выбрать логотип из ${LOGOS.length} вариантов`; logoBtn.style.cssText = 'margin-left:4px;cursor:pointer;font-weight:bold;'; logoBtn.addEventListener('click', showLogoSelector); const container = element.parentNode; container.insertBefore(logoBtn, container.firstChild); buttonAdded = true; console.log('Logo Replacer: button added as first successfully!'); } function showLogoSelector() { console.log('Logo Replacer: showing logo selector'); if (logoPanel) { logoPanel.remove(); logoPanel = null; return; } logoPanel = document.createElement('div'); logoPanel.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border: 3px solid #4a6782; border-radius: 10px; padding: 15px; z-index: 10000; box-shadow: 0 8px 25px rgba(0,0,0,0.3); max-width: 95vw; max-height: 85vh; overflow-y: auto; font-family: Arial, sans-serif; font-size: 12px; `; const title = document.createElement('div'); title.textContent = `Выберите логотип (${LOGOS.length} вариантов):`; title.style.cssText = 'font-weight: bold; margin-bottom: 12px; color: #333; font-size: 14px; text-align: center;'; logoPanel.appendChild(title); const grid = document.createElement('div'); grid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px;'; logoPanel.appendChild(grid); LOGOS.forEach((logo, index) => { const logoContainer = document.createElement('div'); logoContainer.style.cssText = 'border: 1px solid #ddd; border-radius: 6px; padding: 8px; text-align: center; background: #f9f9f9; cursor: pointer;'; const name = document.createElement('div'); name.textContent = logo.description; name.style.cssText = 'font-weight: bold; font-size: 10px; color: #4a6782; margin-bottom: 5px; height: 30px; display: flex; align-items: center; justify-content: center;'; logoContainer.appendChild(name); const preview = document.createElement('img'); preview.src = logo.url; preview.style.cssText = 'max-width: 100px; max-height: 50px; display: block; margin: 0 auto 8px; border: 1px solid #ccc;'; preview.alt = logo.description; logoContainer.appendChild(preview); const logoBtn = document.createElement('button'); logoBtn.textContent = 'Вставить'; logoBtn.style.cssText = ` width: 100%; padding: 4px 8px; background: #4a6782; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 10px; font-weight: bold; `; logoBtn.addEventListener('mouseenter', function() { this.style.background = '#5a7792'; logoContainer.style.background = '#f0f5fa'; }); logoBtn.addEventListener('mouseleave', function() { this.style.background = '#4a6782'; logoContainer.style.background = '#f9f9f9'; }); logoBtn.addEventListener('click', (e) => { e.stopPropagation(); insertLogo(logo.url, logo.description); logoPanel.remove(); logoPanel = null; }); logoContainer.appendChild(logoBtn); logoContainer.addEventListener('click', (e) => { if (e.target !== logoBtn) { insertLogo(logo.url, logo.description); logoPanel.remove(); logoPanel = null; } }); grid.appendChild(logoContainer); }); const closeBtn = document.createElement('button'); closeBtn.textContent = 'Закрыть'; closeBtn.style.cssText = ` display: block; margin: 15px auto 0; padding: 8px 16px; background: #ff4444; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: bold; `; closeBtn.addEventListener('click', () => { logoPanel.remove(); logoPanel = null; }); logoPanel.appendChild(closeBtn); document.body.appendChild(logoPanel); setTimeout(() => { const closeHandler = (e) => { if (logoPanel && !logoPanel.contains(e.target)) { logoPanel.remove(); logoPanel = null; document.removeEventListener('click', closeHandler); } }; document.addEventListener('click', closeHandler); }, 100); } function insertLogo(logoUrl, logoName) { const bbcode = `[center][img]${logoUrl}[/img][/center]`; console.log('Logo Replacer: inserting logo BBCode:', bbcode); let textarea = document.querySelector('textarea[name="Post"]'); if (!textarea) { textarea = document.querySelector('#Post, textarea[id*="post"], textarea[class*="editor"]'); } if (!textarea) { const allTextareas = document.querySelectorAll('textarea'); for (let ta of allTextareas) { if (ta.offsetWidth > 300 && ta.offsetHeight > 100) { textarea = ta; break; } } } if (!textarea) { alert('Не найдено поле для ввода текста! Откройте редактор поста.'); return; } let currentText = textarea.value; // ОЧИСТКА РЕДАКТОРА: Удаляем только старые логотипы currentText = removeAllLogoReferences(currentText); // Вставляем новый логотип в начало const newText = bbcode + '\n\n' + currentText; textarea.value = newText; textarea.scrollTop = 0; textarea.focus(); console.log('Logo Replacer: logo inserted successfully, editor cleaned'); showNotification(`✅ Логотип "${logoName}" добавлен!`); } function removeAllLogoReferences(text) { if (!text) return text; console.log('Logo Replacer: cleaning editor from old logos'); const patterns = [ // Спойлеры с названием "Лого" (точно логотип) /\[spoiler=Лого\][\s\S]*?\[\/spoiler\]/gi, /\[spoiler=.*logo.*\][\s\S]*?\[\/spoiler\]/gi, // Спойлеры без названия, содержащие только логотип в центре /\[spoiler\]\s*\[center\]\s*\[(?:img|attachment)[^\]]*\][\s\S]*?\[\/center\]\s*\[\/spoiler\]/gi, // Логотипы в центре (основной формат вставки) /\[center\]\s*\[(?:img|attachment)[^\]]*logo[^\]]*\]\s*\[\/center\]/gi, /\[center\]\s*\[(?:img|attachment)[^\]]*\]\s*\[\/center\]/gi, // Отдельные изображения логотипов (только если содержат "logo" в URL) /\[(?:img|attachment)\][^\]]*logo[^\]]*\[\/(?:img|attachment)\]/gi, // Вложения логотипов (только если содержат "logo" в названии) /\[attachment[^\]]*logo[^\]]*\]/gi, // Конкретные названия файлов логотипов /\[(?:img|attachment)\][^\]]*(?:Infinity\.png|AICP\.png|AlphaDroid\.png|Ancient\.png|Android\.png)[^\]]*\[\/(?:img|attachment)\]/gi, /\[attachment[^\]]*(?:Infinity\.png|AICP\.png|AlphaDroid\.png|Ancient\.png|Android\.png)[^\]]*\]/gi ]; let cleanedText = text; let totalRemoved = 0; patterns.forEach((pattern, index) => { const matches = cleanedText.match(pattern); if (matches) { console.log(`Logo Replacer: pattern ${index} removed:`, matches); totalRemoved += matches.length; cleanedText = cleanedText.replace(pattern, ''); } }); // Убираем лишние пустые строки только если что-то было удалено if (totalRemoved > 0) { cleanedText = cleanedText.replace(/\n\s*\n\s*\n/g, '\n\n'); cleanedText = cleanedText.replace(/(\r?\n){3,}/g, '\n\n'); cleanedText = cleanedText.trim(); } console.log(`Logo Replacer: total logo references removed from editor: ${totalRemoved}`); return cleanedText; } function showNotification(message) { const oldNotifications = document.querySelectorAll('.logo-replacer-notification'); oldNotifications.forEach(notification => notification.remove()); const notification = document.createElement('div'); notification.className = 'logo-replacer-notification'; notification.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: #4a6782; color: white; padding: 12px 24px; border-radius: 6px; z-index: 10000; font-family: Arial, sans-serif; font-size: 14px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); text-align: center; max-width: 400px; line-height: 1.4; `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 5000); } function init() { console.log(`Logo Replacer v4.3 initialized with ${LOGOS.length} logos`); setTimeout(addLogoButton, 1000); setTimeout(addLogoButton, 3000); setTimeout(addLogoButton, 5000); const observer = new MutationObserver(function(mutations) { for (let mutation of mutations) { if (mutation.addedNodes.length > 0) { setTimeout(addLogoButton, 500); break; } } }); observer.observe(document.body, { childList: true, subtree: true }); setInterval(addLogoButton, 5000); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();