您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Дискорд эмоджики в тунио, теперь лучше
// ==UserScript== // @name Discord Emojis for Toonio // @namespace DiscordEmojisConverter // @version 1.1.03 // @description Дискорд эмоджики в тунио, теперь лучше // @author Vika4ernaya // @match https://toonio.ru/* // @match https://*.toonio.ru/* // @icon https://www.google.com/s2/favicons?sz=64&domain=toonio.ru // @grant none // @license MIT // ==/UserScript== // Добавляем функцию для установки пака function installPack() { const hash = window.location.hash.slice(1); if (!hash) return; try { const packData = JSON.parse(atob(hash)); const { pack, emojis } = packData; // Получаем текущие паки и эмодзи const currentPacks = storage.get('demoji_packs'); const currentEmojis = storage.get('demoji_emojis'); // Проверяем, существует ли уже пак с таким ID const existingPack = currentPacks.find(p => p.id === pack.id); if (existingPack) { if (!confirm(`Пак "${pack.name}" уже существует. Хотите обновить его?`)) { return; } // Удаляем старые эмодзи пака const newEmojis = currentEmojis.filter(e => e.packId !== pack.id); storage.set('demoji_emojis', [...newEmojis, ...emojis]); // Обновляем информацию о паке const newPacks = currentPacks.map(p => p.id === pack.id ? pack : p); storage.set('demoji_packs', newPacks); } else { // Добавляем новый пак и его эмодзи storage.set('demoji_packs', [...currentPacks, pack]); storage.set('demoji_emojis', [...currentEmojis, ...emojis]); } alert(`Пак "${pack.name}" успешно ${existingPack ? 'обновлен' : 'установлен'}!`); // Очищаем хэш из URL window.location.hash = ''; // Обновляем меню updateDemojiMenu(); } catch (error) { console.error('Ошибка установки пака:', error); alert('Ошибка при установке пака. Неверный формат данных.'); } } // Добавляем проверку URL при загрузке страницы document.addEventListener('DOMContentLoaded', () => { if (window.location.hash.startsWith('#')) { installPack(); } }); const storage = { get: (key) => JSON.parse(localStorage.getItem(key) || '[]'), set: (key, value) => localStorage.setItem(key, JSON.stringify(value)), }; // Создание UI для эмодзи function createDemojiUI() { const formatContainer = document.querySelector(".send .format"); const editorFormatCont = document.querySelector("#save .format"); if (!formatContainer && !editorFormatCont) return; // Создаем кнопку const demojiButton = document.createElement("a"); demojiButton.id = "discordDemojiBtn"; demojiButton.title = "Discord эмодзи"; demojiButton.style.width = "24px"; demojiButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" style="fill: white;"><path d="M 18.90625 7 C 18.90625 7 12.539063 7.4375 8.375 10.78125 C 8.355469 10.789063 8.332031 10.800781 8.3125 10.8125 C 7.589844 11.480469 7.046875 12.515625 6.375 14 C 5.703125 15.484375 4.992188 17.394531 4.34375 19.53125 C 3.050781 23.808594 2 29.058594 2 34 C 1.996094 34.175781 2.039063 34.347656 2.125 34.5 C 3.585938 37.066406 6.273438 38.617188 8.78125 39.59375 C 11.289063 40.570313 13.605469 40.960938 14.78125 41 C 15.113281 41.011719 15.429688 40.859375 15.625 40.59375 L 18.0625 37.21875 C 20.027344 37.683594 22.332031 38 25 38 C 27.667969 38 29.972656 37.683594 31.9375 37.21875 L 34.375 40.59375 C 34.570313 40.859375 34.886719 41.011719 35.21875 41 C 36.394531 40.960938 38.710938 40.570313 41.21875 39.59375 C 43.726563 38.617188 46.414063 37.066406 47.875 34.5 C 47.960938 34.347656 48.003906 34.175781 48 34 C 48 29.058594 46.949219 23.808594 45.65625 19.53125 C 45.007813 17.394531 44.296875 15.484375 43.625 14 C 42.953125 12.515625 42.410156 11.480469 41.6875 10.8125 C 41.667969 10.800781 41.644531 10.789063 41.625 10.78125 C 37.460938 7.4375 31.09375 7 31.09375 7 C 31.019531 6.992188 30.949219 6.992188 30.875 7 C 30.527344 7.046875 30.234375 7.273438 30.09375 7.59375 C 30.09375 7.59375 29.753906 8.339844 29.53125 9.40625 C 27.582031 9.09375 25.941406 9 25 9 C 24.058594 9 22.417969 9.09375 20.46875 9.40625 C 20.246094 8.339844 19.90625 7.59375 19.90625 7.59375 C 19.734375 7.203125 19.332031 6.964844 18.90625 7 Z M 18.28125 9.15625 C 18.355469 9.359375 18.40625 9.550781 18.46875 9.78125 C 16.214844 10.304688 13.746094 11.160156 11.4375 12.59375 C 11.074219 12.746094 10.835938 13.097656 10.824219 13.492188 C 10.816406 13.882813 11.039063 14.246094 11.390625 14.417969 C 11.746094 14.585938 12.167969 14.535156 12.46875 14.28125 C 17.101563 11.410156 22.996094 11 25 11 C 27.003906 11 32.898438 11.410156 37.53125 14.28125 C 37.832031 14.535156 38.253906 14.585938 38.609375 14.417969 C 38.960938 14.246094 39.183594 13.882813 39.175781 13.492188 C 39.164063 13.097656 38.925781 12.746094 38.5625 12.59375 C 36.253906 11.160156 33.785156 10.304688 31.53125 9.78125 C 31.59375 9.550781 31.644531 9.359375 31.71875 9.15625 C 32.859375 9.296875 37.292969 9.894531 40.3125 12.28125 C 40.507813 12.460938 41.1875 13.460938 41.8125 14.84375 C 42.4375 16.226563 43.09375 18.027344 43.71875 20.09375 C 44.9375 24.125 45.921875 29.097656 45.96875 33.65625 C 44.832031 35.496094 42.699219 36.863281 40.5 37.71875 C 38.5 38.496094 36.632813 38.84375 35.65625 38.9375 L 33.96875 36.65625 C 34.828125 36.378906 35.601563 36.078125 36.28125 35.78125 C 38.804688 34.671875 40.15625 33.5 40.15625 33.5 C 40.570313 33.128906 40.605469 32.492188 40.234375 32.078125 C 39.863281 31.664063 39.226563 31.628906 38.8125 32 C 38.8125 32 37.765625 32.957031 35.46875 33.96875 C 34.625 34.339844 33.601563 34.707031 32.4375 35.03125 C 32.167969 35 31.898438 35.078125 31.6875 35.25 C 29.824219 35.703125 27.609375 36 25 36 C 22.371094 36 20.152344 35.675781 18.28125 35.21875 C 18.070313 35.078125 17.8125 35.019531 17.5625 35.0625 C 16.394531 34.738281 15.378906 34.339844 14.53125 33.96875 C 12.234375 32.957031 11.1875 32 11.1875 32 C 10.960938 31.789063 10.648438 31.699219 10.34375 31.75 C 9.957031 31.808594 9.636719 32.085938 9.53125 32.464844 C 9.421875 32.839844 9.546875 33.246094 9.84375 33.5 C 9.84375 33.5 11.195313 34.671875 13.71875 35.78125 C 14.398438 36.078125 15.171875 36.378906 16.03125 36.65625 L 14.34375 38.9375 C 13.367188 38.84375 11.5 38.496094 9.5 37.71875 C 7.300781 36.863281 5.167969 35.496094 4.03125 33.65625 C 4.078125 29.097656 5.0625 24.125 6.28125 20.09375 C 6.90625 18.027344 7.5625 16.226563 8.1875 14.84375 C 8.8125 13.460938 9.492188 12.460938 9.6875 12.28125 C 12.707031 9.894531 17.140625 9.296875 18.28125 9.15625 Z M 18.5 21 C 15.949219 21 14 23.316406 14 26 C 14 28.683594 15.949219 31 18.5 31 C 21.050781 31 23 28.683594 23 26 C 23 23.316406 21.050781 21 18.5 21 Z M 31.5 21 C 28.949219 21 27 23.316406 27 26 C 27 28.683594 28.949219 31 31.5 31 C 34.050781 31 36 28.683594 36 26 C 36 23.316406 34.050781 21 31.5 21 Z M 18.5 23 C 19.816406 23 21 24.265625 21 26 C 21 27.734375 19.816406 29 18.5 29 C 17.183594 29 16 27.734375 16 26 C 16 24.265625 17.183594 23 18.5 23 Z M 31.5 23 C 32.816406 23 34 24.265625 34 26 C 34 27.734375 32.816406 29 31.5 29 C 30.183594 29 29 27.734375 29 26 C 29 24.265625 30.183594 23 31.5 23 Z"></path></svg> `; // Создаем меню const demojiMenu = document.createElement("div"); demojiMenu.id = "discordDemojiMenu"; demojiMenu.className = "discordDemojis"; demojiMenu.style.display = 'none'; demojiMenu.innerHTML = ` <style> .discordDemojis { background: #292929; padding: 3px; max-width: 530px; display: none; position: absolute; top: 130px; left: 196px; z-index: 99; border-radius: 5px; } .toons_picker_relative .discordDemojis { top: inherit; bottom: 0; } .format { grid-template-columns: repeat(6, 24px) 1fr !important; } .discordDemojis > .demoji-content { max-height: 105px; overflow-y: auto; width: 35px; height: 30px; display: inline-block; margin: 5px; padding: 5px; background-color: #0003; border-radius: 3px; cursor: pointer; } .discordDemoji { width: 40px; height: 40px; cursor: pointer; transition: all 0.1s ease-in-out; border: 2px solid #0000; opacity: 0.7; } .discordDemoji:hover { border: 2px solid #7289da; opacity: 1; border-radius: 4px; } .demoji-header { display: flex; gap: 10px; margin-bottom: 10px; padding: 5px; } .demoji_search { flex: 1; display: flex; align-items: center; background: #0003; padding: 5px; border-radius: 3px; } .demoji_search input { background: transparent; border: none; color: white; margin-left: 5px; width: 100%; } .demoji_search input:focus { outline: none; } .demoji_notfound { display: none; text-align: center; padding: 10px; color: white; } #demojiArea { overflow-y: scroll; height: 260px; scroll-behavior: smooth; } .pack-header{ font-family: Fira Sans, sans-serif; padding: 5px; cursor: pointer; user-select: none; color: white; display: flex; align-items: center; gap: 5px; } .pack-header img{ width: 30px; height: 30px; vertical-align: middle; } .pack-header .arrow { margin-left: auto; transition: transform 0.2s ease; } .pack-collapsed .pack-header .arrow { transform: rotate(-90deg); } .pack-emojis { overflow: hidden; transition: all 0.2s ease-in-out; padding: 5px; } .pack-collapsed .pack-emojis { height: 0 !important; padding: 0 5px !important; } .pack-icons-sidebar { display: flex; flex-direction: column; gap: 5px; padding: 5px; background: #1e1e1e; border-radius: 4px; } .pack-icon { margin-top: 5px; width: 40px; height: 40px; cursor: pointer; border-radius: 4px; transition: all 0.2s ease; border: 2px solid #0000; opacity: 0.7; border-radius: 4px; } .pack-icon:hover { border: 2px solid #7289da; opacity: 1; } .pack-icon.active { opacity: 1; background: #7289da33; } .packs-content { flex: 1; } .demoji-content-area{ display: flex; flex-direction: row; gap: 10px; height: 300px; overflow: hidden; overflow-x: auto; scroll-behavior: smooth; padding: 5px; width: 502px; } </style> <div class="demoji-content-area"> <div class="pack-icons-sidebar" id="packIconsSidebar"></div> <div class="packs-content"> <div class="demoji-header"> <div class="demoji_search"> <span class="fas fa-search fa-fw"></span> <input type="text" placeholder="Искать эмодзи" id="demojiSearchInput"> </div> <div class="demoji-controls"> <a id="addDiscordDemojiBtn" class="add-emoji-btn" title="Добавить эмодзи"><span class="far fa-plus fa-fw icon"></span></a> <a id="addPackBtn" class="add-emoji-btn" title="Добавить пак"><span class="far fa-folder-plus fa-fw icon"></span></a> <a id="importPackBtn" class="add-emoji-btn" title="Импортировать пак"><span class="far fa-upload fa-fw icon"></span></a> <a id="aboutDiscordDemoji" class="add-emoji-btn" title="Как пользоваться"><span class="far fa-question fa-fw"></span></a> </div> </div> <div id="demojiArea"></div> <div class="demoji_notfound" id="demojiNotFound"> <h3>Ничего не найдено</h3> </div> </div> </div> `; // Добавляем кнопку в .format if (editorFormatCont) { editorFormatCont.appendChild(demojiButton); } else { formatContainer.appendChild(demojiButton); } demojiMenu.querySelector('#addPackBtn').addEventListener('click', showAddPackDialog); // Добавляем меню в .emoji_holder if (document.querySelector(".emoji_holder")) { document.querySelector(".emoji_holder").appendChild(demojiMenu); } else if (document.querySelector(".toons_picker_relative")) { document.querySelector(".toons_picker_relative").appendChild(demojiMenu); } else if (editorFormatCont) { demojiMenu.classList.add("descr"); document.querySelector(".tpicker.descr").insertAdjacentElement('afterend', demojiMenu); } else { console.error("Не удалось найти контейнер для Discord эмодзи меню"); return; } // Обработчики событий document.getElementById('discordDemojiBtn').addEventListener('click', toggleDemojiMenu); document.getElementById('addDiscordDemojiBtn').addEventListener('click', showAddDemojiDialog); document.getElementById('aboutDiscordDemoji').addEventListener('click', showDemojiHelp); document.getElementById('demojiSearchInput').addEventListener('input', searchDemojis); document.getElementById('importPackBtn').addEventListener('click', importPack); // Закрытие меню при клике вне его document.addEventListener('click', (e) => { const menu = document.getElementById('discordDemojiMenu'); const btn = document.getElementById('discordDemojiBtn'); if (!menu.contains(e.target) && !btn.contains(e.target)) { menu.style.display = 'none'; } }); updateDemojiMenu(); } function toggleDemojiMenu() { const menu = document.getElementById('discordDemojiMenu'); menu.style.display = menu.style.display === 'none' ? 'block' : 'none'; } function searchDemojis(event) { const searchText = event.target.value.toLowerCase(); const demojiArea = document.getElementById('demojiArea'); const notFoundMessage = document.getElementById('demojiNotFound'); const packs = storage.get('demoji_packs'); const emojis = storage.get('demoji_emojis'); const filteredEmojis = searchText ? emojis.filter(emoji => emoji.name.toLowerCase().includes(searchText) || packs.find(p => p.id === emoji.packId)?.name.toLowerCase().includes(searchText) ) : emojis; if (filteredEmojis.length === 0) { demojiArea.style.display = 'none'; notFoundMessage.style.display = 'block'; } else { demojiArea.style.display = 'block'; notFoundMessage.style.display = 'none'; // Группируем эмодзи по пакам const groupedEmojis = {}; filteredEmojis.forEach(emoji => { if (!groupedEmojis[emoji.packId]) { groupedEmojis[emoji.packId] = []; } groupedEmojis[emoji.packId].push(emoji); }); // Отображаем эмодзи по пакам demojiArea.innerHTML = packs .sort((a, b) => a.order - b.order) .map(pack => { if (!groupedEmojis[pack.id]) return ''; return ` <div class="pack-container"> <div class="pack-header" data-pack-header="${pack.id}"> <div class="pack-header-content" onclick=" if(!event.target.parentElement.classList.contains('pack-buttons') && !event.target.parentElement.parentElement.classList.contains('pack-buttons')) togglePack('${pack.id}')"> <img src="${pack.icon}" oncontextmenu="editPackIcon('${pack.id}', event)" title="ПКМ чтобы изменить иконку"> <span oncontextmenu="editPackName('${pack.id}', event)" title="ПКМ чтобы изменить название">${pack.name}</span> <span class="arrow"> <i class="fas fa-chevron-down"></i> </span> <div class="pack-buttons"> <button class="pack-btn add-emoji-btn" onclick="addEmojiToPackK('${pack.id}')" title="Добавить эмодзи в пак"> <i class="fas fa-plus"></i> </button> <button class="pack-btn share-pack-btn" onclick="exportPack('${pack.id}')" title="Экспортировать пак"> <i class="fas fa-download"></i> </button> <button class="pack-btn delete-pack-btn" onclick="deletePack('${pack.id}')" title="Удалить пак"> <i class="fas fa-trash-alt"></i> </button> </div> </div> </div> <div class="pack-emojis"> ${groupedEmojis[pack.id].map(emoji => ` <img src="https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'webp'}" alt=":${emoji.name}:" title=":${emoji.name}:" class="discordDemoji" data-id="${emoji.id}" onclick="T.AddText('<${emoji.animated ? 'a' : ''}:${emoji.name}:${emoji.id}'>, ${document.querySelector(".send .format") ? "false" : "true"})" onmousedown="handleDemojiMouseDown(event, '${emoji.id}')" > `).join('')} </div> </div> `; }).join(''); } } function updateDemojiMenu() { const demojiArea = document.getElementById('demojiArea'); const packIconsSidebar = document.getElementById('packIconsSidebar'); const packs = storage.get('demoji_packs'); const emojis = storage.get('demoji_emojis'); const searchText = document.getElementById('demojiSearchInput').value.toLowerCase(); // Обновляем сайдбар с иконками packIconsSidebar.innerHTML = packs .sort((a, b) => a.order - b.order) .map(pack => ` <img src="${pack.icon}" class="pack-icon" data-pack-id="${pack.id}" title="${pack.name}" onclick="scrollToPack('${pack.id}')" > `).join(''); const filteredEmojis = searchText ? emojis.filter(emoji => emoji.name.toLowerCase().includes(searchText) || packs.find(p => p.id === emoji.packId)?.name.toLowerCase().includes(searchText) ) : emojis; if (filteredEmojis.length === 0) { demojiArea.style.display = 'none'; document.getElementById('demojiNotFound').style.display = 'block'; return; } demojiArea.style.display = 'block'; document.getElementById('demojiNotFound').style.display = 'none'; // Группируем эмодзи по пакам const groupedEmojis = {}; filteredEmojis.forEach(emoji => { if (!groupedEmojis[emoji.packId]) { groupedEmojis[emoji.packId] = []; } groupedEmojis[emoji.packId].push(emoji); }); // Отображаем эмодзи по пакам demojiArea.innerHTML = packs .sort((a, b) => a.order - b.order) .map(pack => { const packEmojis = groupedEmojis[pack.id] || []; // Сортируем эмодзи: сначала статические, потом анимированные const sortedEmojis = packEmojis.sort((a, b) => { if (a.animated === b.animated) { return a.name.localeCompare(b.name); // Сортировка по имени внутри каждой группы } return a.animated ? 1 : -1; // Анимированные идут после статических }); return ` <div class="pack-container"> <div class="pack-header" data-pack-header="${pack.id}"> <div class="pack-header-content" onclick=" if(!event.target.parentElement.classList.contains('pack-buttons') && !event.target.parentElement.parentElement.classList.contains('pack-buttons')) togglePack('${pack.id}')"> <img src="${pack.icon}" oncontextmenu="editPackIcon('${pack.id}', event)" title="ПКМ чтобы изменить иконку"> <span oncontextmenu="editPackName('${pack.id}', event)" title="ПКМ чтобы изменить название">${pack.name}</span> <span class="arrow"> <i class="fas fa-chevron-down"></i> </span> <div class="pack-buttons"> <button class="pack-btn add-emoji-btn" onclick="addEmojiToPackK('${pack.id}')" title="Добавить эмодзи в пак"> <i class="fas fa-plus"></i> </button> <button class="pack-btn share-pack-btn" onclick="exportPack('${pack.id}')" title="Экспортировать пак"> <i class="fas fa-download"></i> </button> <button class="pack-btn delete-pack-btn" onclick="deletePack('${pack.id}')" title="Удалить пак"> <i class="fas fa-trash-alt"></i> </button> </div> </div> </div> <div class="pack-emojis"> ${packEmojis.length > 0 ? packEmojis.map(emoji => ` <img src="https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'webp'}" alt=":${emoji.name}:" title=":${emoji.name}:" class="discordDemoji" data-id="${emoji.id}" onclick="T.AddText('<${emoji.animated ? 'a' : ''}:${emoji.name}:${emoji.id}>', ${document.querySelector(".send .format") ? "false" : "true"})" onmousedown="handleDemojiMouseDown(event, '${emoji.id}')" > `).join('') : '<div class="no-emojis-message">В этом паке пока нет эмодзи</div>' } </div> </div> `; }).join(''); } function showAddDemojiDialog() { const packs = storage.get('demoji_packs'); if (packs.length === 0) { alert('Сначала создайте хотя бы один пак!'); return; } const packId = prompt(`Выберите ID пака для добавления:\n${packs.map(p => `${p.id}: ${p.name}`).join('\n')}`); if (!packId || !packs.find(p => p.id === packId)) { alert('Неверный ID пака!'); return; } addEmojiToPackK(packId) } function addEmojiToPackK(packId) { const input = prompt('Вставьте ID Discord эмодзи (можно несколько через пробел)\nФорматы: id или id:name'); if (!input) return; const items = input.split(/\s+/).filter(item => item.length > 0); items.forEach(item => { let id, name; if (item.includes(':')) { [id, name] = item.split(':'); } else { id = item; name = id.slice(0, 4); } if (!/^\d+$/.test(id)) { alert(`Неверный формат ID эмодзи: ${id}`); return; } checkAndAddEmoji(id, name, packId); }); } // Проверка и добавление эмодзи function checkAndAddEmoji(id, name, packId) { const img = new Image(); img.onload = function () { addEmojiToPack(id, name, packId, true); }; img.onerror = function () { const staticImg = new Image(); staticImg.onload = function () { addEmojiToPack(id, name, packId, false); }; staticImg.onerror = function () { alert(`Эмодзи с ID ${id} не найден!`); }; staticImg.src = `https://cdn.discordapp.com/emojis/${id}.webp`; }; img.src = `https://cdn.discordapp.com/emojis/${id}.gif`; } // Добавление эмодзи в пак function addEmojiToPack(id, name, packId, animated) { const emojis = storage.get('demoji_emojis') || []; emojis.push({ id: id, name: name, packId: packId, animated: animated }); storage.set('demoji_emojis', emojis); updateDemojiMenu(); } function showDemojiHelp() { alert(`Как пользоваться Discord эмодзи 📦 Управление паками • Добавить пак: нажмите кнопку "+" в верхнем меню • Удалить пак: нажмите кнопку корзины в заголовке пака • Поделиться паком: нажмите кнопку "экспорт" в заголовке пака • Установить пак: нажмите кнопку "импорт" в верхнем меню и выберите файл 😀 Управление эмодзи • Добавить эмодзи: нажмите "+" в заголовке пака и вставьте ссылку или ID эмодзи • Использовать эмодзи: кликните левой кнопкой мыши по эмодзи • Переименовать эмодзи: нажмите среднюю кнопку мыши (колесико) • Удалить эмодзи: нажмите правую кнопку мыши 🔍 Поиск • Используйте поисковую строку для поиска по имени эмодзи или названию пака • Поиск работает в реальном времени 💡 Дополнительно • Паки можно сворачивать/разворачивать, кликнув по заголовку • Быстрая навигация по пакам доступна через боковую панель • Для добавления эмодзи из Discord, скопируйте ID эмодзи (\\:emoji:) • Поддерживаются как статические, так и анимированные эмодзи ⚠️ Важно • Эмодзи сохраняются локально в вашем браузере • При очистке данных браузера, все сохраненные эмодзи будут удалены • Рекомендуется периодически экспортировать паки для создания резервной копии`); } window.removeDiscordDemoji = function (demojiId) { if (confirm('Удалить этот эмодзи?')) { const emojis = storage.get('demoji_emojis'); const newEmojis = emojis.filter(e => e.id !== demojiId); storage.set('demoji_emojis', newEmojis); updateDemojiMenu(); } }; // Конвертация эмодзи в комментариях function convertDiscordDemojis(element) { if (!element) return; element.innerHTML = element.innerHTML.replace( /<(a?):(\w+):(\d+)>|<(a?):(\w+):(\d+)>/g, (match, animated1, name1, id1, animated2, name2, id2) => { const animated = animated1 || animated2; const name = name1 || name2; const id = id1 || id2; const extension = animated ? 'gif' : 'webp'; return `<img src="https://cdn.discordapp.com/emojis/${id}.${extension}" alt=":${name}:" class="discord-demoji" style="" data-demoji-id="${id}" data-demoji-name="${name}" data-demoji-animated="${!!animated}" oncontextmenu="copyEmojiInfo(event, '${id}', '${name}')" >`; } ); } // Обработка существующих комментариев function processExistingComments() { const comments = document.querySelectorAll('div.comment_data > p'); comments.forEach(convertDiscordDemojis); const desc = document.querySelectorAll(".description:not(textarea)"); desc.forEach(convertDiscordDemojis); const toonName = document.querySelectorAll(".content.divided.watch .info h1"); toonName.forEach(convertDiscordDemojis); const smallToonName = document.querySelectorAll(".toon_data .name a"); smallToonName.forEach(convertDiscordDemojis); } // Наблюдатель за новыми комментариями const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { const comments = node.querySelectorAll('div.comment_data > p'); const desc = node.querySelectorAll(".description:not(textarea)"); const toonName = node.querySelectorAll(".content.divided.watch .info h1"); const smallToonName = node.querySelectorAll(".toon_data .name a"); comments.forEach(convertDiscordDemojis); desc.forEach(convertDiscordDemojis); toonName.forEach(convertDiscordDemojis); smallToonName.forEach(convertDiscordDemojis); } }); }); }); // Инициализация function init() { try { const additionalStyles = ` .no-emojis-message { padding: 10px; text-align: center; color: #666; font-family: 'Pangolin', sans-serif; border-radius: 4px; margin: 5px; } .discord-demoji{ width: 40px; height: 40px; vertical-align: middle; } .toon_data .discord-demoji{ width: 20px !important; height: 20px !important; } .pack-header { display: flex; justify-content: space-between; align-items: center; padding: 5px; border-radius: 4px; } .pack-container{ background: #2f3136; } .pack-header-content { display: grid; grid-template-columns: auto 1fr 1fr auto; gap: 10px; align-items: center; width: 100%; } .pack-header-content span { opacity: 0.7; transition: all 0.2s ease; } .pack-header-content:hover span { opacity: 1; } .add-emoji-btn { background: transparent; border: none; color: #b9bbbe; padding: 5px; cursor: pointer; border-radius: 4px; transition: all 0.2s ease; width: 100%; item-align: center; text-align: center; } .add-emoji-btn span{ margin: 0; } .add-emoji-btn:hover { color: white; background: #40444b; } .pack-buttons { display: flex; gap: 5px; margin-left: auto; } .pack-btn { background: transparent; border: none; color: #b9bbbe; padding: 5px; cursor: pointer; border-radius: 4px; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; } .pack-btn:hover { color: white; background: #40444b; } .share-pack-btn { color: #b9bbbe; } .share-pack-btn:hover { color: #00b0f4; } .pack-header-content img { width: 24px; height: 24px; border-radius: 4px; } .delete-pack-btn { color: #b9bbbe; } .delete-pack-btn:hover { color: #ed4245; background: rgba(237, 66, 69, 0.1); } .emoji-copy-notification { position: fixed; background: #36393f; color: white; padding: 8px 12px; border-radius: 4px; font-size: 14px; z-index: 9999; pointer-events: none; animation: fadeInOut 1.5s ease forwards; transform: translate(-50%, -100%); margin-top: -10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); font-family: 'Pangolin', sans-serif; } @keyframes fadeInOut { 0% { opacity: 0; transform: translate(-50%, -90%); } 15% { opacity: 1; transform: translate(-50%, -100%); } 85% { opacity: 1; transform: translate(-50%, -100%); } 100% { opacity: 0; transform: translate(-50%, -90%); } } `; document.querySelector('style').textContent += additionalStyles; } catch { } processExistingComments(); if (document.querySelector("#comment_form") || document.querySelector(".sender") || document.querySelector("iframe#draw")) { createDemojiUI(); } observer.observe(document.body, { childList: true, subtree: true }); } // Запуск после загрузки страницы if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Добавляем глобальную функцию для обработки нажатий мыши window.handleDemojiMouseDown = function (event, demojiId) { event.preventDefault(); console.log(event.button) if (event.button === 1) { // Средняя кнопка мыши renameDemoji(demojiId); } else if (event.button === 2) { // Правая кнопка мыши removeDiscordDemoji(demojiId); } }; function renameDemoji(demojiId) { console.log(demojiId); const emojis = storage.get('demoji_emojis'); const emoji = emojis.find(e => e.id === demojiId); if (!emoji) return; const newName = prompt('Введите новое имя для эмодзи:', emoji.name); if (newName && newName.trim()) { emoji.name = newName.trim(); storage.set('demoji_emojis', emojis); updateDemojiMenu(); } } function showAddPackDialog() { const packName = prompt('Введите название пака:'); if (!packName) return; const packIcon = prompt('Вставьте URL иконки пака:'); if (!packIcon) return; const packs = storage.get('demoji_packs'); const newPack = { id: Date.now().toString(), name: packName, icon: packIcon, order: packs.length // Для сортировки паков }; packs.push(newPack); storage.set('demoji_packs', packs); updateDemojiMenu(); alert('Пак успешно создан!\nID пака: ' + newPack.id); } window.togglePack = togglePack; // Добавляем стили // Добавляем в глобальную область видимости window.addEmojiToPackK = addEmojiToPackK; function togglePack(packId) { const packContainer = document.querySelector(`[data-pack-header="${packId}"]`).closest('.pack-container'); const emojisContainer = packContainer.querySelector('.pack-emojis'); // Сохраняем текущее состояние const isCollapsed = packContainer.classList.contains('pack-collapsed'); if (!isCollapsed) { // Сворачиваем const currentHeight = emojisContainer.offsetHeight; emojisContainer.style.height = currentHeight + 'px'; // Форсируем reflow emojisContainer.offsetHeight; packContainer.classList.add('pack-collapsed'); } else { // Разворачиваем emojisContainer.style.transition = 'none'; packContainer.classList.remove('pack-collapsed'); const targetHeight = emojisContainer.scrollHeight; packContainer.classList.add('pack-collapsed'); emojisContainer.style.height = '0px'; // Форсируем reflow emojisContainer.offsetHeight; emojisContainer.style.transition = ''; packContainer.classList.remove('pack-collapsed'); emojisContainer.style.height = targetHeight + 'px'; } // Очищаем высоту после анимации emojisContainer.addEventListener('transitionend', function handler() { if (!packContainer.classList.contains('pack-collapsed')) { emojisContainer.style.height = ''; } emojisContainer.removeEventListener('transitionend', handler); }); } function scrollToPack(packId) { const packElement = document.querySelector(`[data-pack-header="${packId}"]`); if (packElement) { // Обновляем активную иконку document.querySelectorAll('.pack-icon').forEach(icon => { icon.classList.remove('active'); }); document.querySelector(`.pack-icon[data-pack-id="${packId}"]`).classList.add('active'); // Прокручиваем к паку const demojiArea = document.getElementById('demojiArea'); const packTop = packElement.offsetTop; demojiArea.scrollTo({ top: packTop, behavior: 'smooth' }); // Разворачиваем пак, если он свёрнут const packContainer = packElement.closest('.pack-container'); if (packContainer.classList.contains('pack-collapsed')) { togglePack(packId); } } } // Добавляем в глобальную область видимости window.scrollToPack = scrollToPack; window.deletePack = deletePack; // Добавляем функцию удаления пака function deletePack(packId) { const packs = storage.get('demoji_packs'); const pack = packs.find(p => p.id === packId); if (!pack) return; if (!confirm(`Вы уверены, что хотите удалить пак "${pack.name}"?\nВсе эмодзи пака также будут удалены.`)) { return; } // Удаляем пак const newPacks = packs.filter(p => p.id !== packId); storage.set('demoji_packs', newPacks); // Удаляем все эмодзи пака const emojis = storage.get('demoji_emojis'); const newEmojis = emojis.filter(e => e.packId !== packId); storage.set('demoji_emojis', newEmojis); updateDemojiMenu(); alert(`Пак "${pack.name}" успешно удален!`); } // Добавляем в глобальную область видимости window.deletePack = deletePack; // Функция экспорта пака function exportPack(packId) { const packs = storage.get('demoji_packs'); const emojis = storage.get('demoji_emojis'); const pack = packs.find(p => p.id === packId); const packEmojis = emojis.filter(e => e.packId === packId); if (!pack || !packEmojis.length) { alert('Ошибка: пак не найден или пуст'); return; } const packData = { pack: pack, emojis: packEmojis }; const blob = new Blob([JSON.stringify(packData)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${pack.name}_pack.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // Функция импорта пака function importPack() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = function (e) { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = function (e) { try { const packData = JSON.parse(e.target.result); const { pack, emojis } = packData; // Получаем текущие паки и эмодзи const currentPacks = storage.get('demoji_packs'); const currentEmojis = storage.get('demoji_emojis'); // Проверяем, существует ли уже пак с таким ID const existingPack = currentPacks.find(p => p.id === pack.id); if (existingPack) { if (!confirm(`Пак "${pack.name}" уже существует. Заменить?`)) { return; } // Удаляем старые эмодзи пака const newEmojis = currentEmojis.filter(e => e.packId !== pack.id); storage.set('demoji_emojis', [...newEmojis, ...emojis]); // Обновляем пак const newPacks = currentPacks.map(p => p.id === pack.id ? pack : p); storage.set('demoji_packs', newPacks); } else { // Добавляем новый пак и его эмодзи storage.set('demoji_packs', [...currentPacks, pack]); storage.set('demoji_emojis', [...currentEmojis, ...emojis]); } updateDemojiMenu(); alert(`Пак "${pack.name}" успешно импортирован!`); } catch (error) { console.error('Ошибка импорта пака:', error); alert('Ошибка при импорте пака. Неверный формат файла.'); } }; reader.readAsText(file); }; input.click(); } window.exportPack = exportPack; function editPackName(packId, event) { event.preventDefault(); // Предотвращаем стандартное контекстное меню const packs = storage.get('demoji_packs'); const pack = packs.find(p => p.id === packId); if (!pack) return; const newName = prompt('Введите новое название пака:', pack.name); if (newName && newName.trim()) { pack.name = newName.trim(); storage.set('demoji_packs', packs); updateDemojiMenu(); } } function editPackIcon(packId, event) { event.preventDefault(); // Предотвращаем стандартное контекстное меню const packs = storage.get('demoji_packs'); const pack = packs.find(p => p.id === packId); if (!pack) return; const newIcon = prompt('Вставьте новый URL иконки пака:', pack.icon); if (newIcon && newIcon.trim()) { pack.icon = newIcon.trim(); storage.set('demoji_packs', packs); updateDemojiMenu(); } } // Добавьте в глобальную область видимости window.editPackName = editPackName; window.editPackIcon = editPackIcon; // Добавьте функцию для копирования в буфер обмена function copyEmojiInfo(event, emojiId, emojiName) { event.preventDefault(); // Предотвращаем стандартное контекстное меню const emojiInfo = `${emojiId}:${emojiName}`; navigator.clipboard.writeText(emojiInfo).then(() => { // Создаем временное уведомление const notification = document.createElement('div'); notification.className = 'emoji-copy-notification'; notification.textContent = 'Информация скопирована'; // Позиционируем уведомление возле курсора notification.style.left = `${event.pageX}px`; notification.style.top = `${event.pageY}px`; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 1500); }); } window.copyEmojiInfo = copyEmojiInfo;