// ==UserScript==
// @name 호감이모티콘
// @namespace http://tampermonkey.net/
// @version 1.0
// @description -
// @author You
// @match https://play.sooplive.co.kr/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=sooplive.co.kr
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const AREA_ID = 'dynamic_area';
const TOGGLEBAR_ID = 'dynamic_toggle';
const FAVORITE_KEY = 'fav_emoticons';
const HEIGHT_KEY = 'dynamic_area_height';
const DEFAULT_DYNAMIC_HEIGHT = 220;
const MIN_HEIGHT = 50;
const MAX_HEIGHT = 300;
const container = document.querySelector('#emoticonContainer');
if (container) {
container.style.transition = 'none';
container.style.animation = 'none';
container.classList.remove(...container.classList);
const head = container.querySelector('#emoticonBox > div.head');
if (head) { head.innerHTML = ''; head.style.height = '10px'; head.style.padding = '0'; }
}
function hideEmoticonContainer() {
const container = document.querySelector('#emoticonContainer');
if (!container) return;
container.style.transition = 'opacity 0.3s ease';
container.style.opacity = '0';
container.style.pointerEvents = 'none';
container.addEventListener('transitionend', function handler() {
container.style.display = 'none';
container.removeEventListener('transitionend', handler);
});
}
function showEmoticonContainer() {
const container = document.querySelector('#emoticonContainer');
if (!container) return;
positionEmoticonContainer();
container.style.display = 'block';
container.style.opacity = '0';
container.getBoundingClientRect();
container.style.transition = 'opacity 0.3s ease';
container.style.opacity = '1';
container.style.pointerEvents = 'auto';
}
const btnEmo = document.querySelector('#btn_emo');
if (btnEmo) {
btnEmo.addEventListener('click', () => {
const container = document.querySelector('#emoticonContainer');
if (!container) return;
if (container.style.display === 'block') hideEmoticonContainer();
else showEmoticonContainer();
});
}
function positionEmoticonContainer() {
const dynamicArea = document.querySelector('#dynamic_area');
const container = document.querySelector('#emoticonContainer');
if (!dynamicArea || !container) return;
const rect = dynamicArea.getBoundingClientRect();
const popupHeight = 300;
container.style.position = 'fixed';
container.style.top = `${rect.top - popupHeight - 10}px`;
container.style.left = `${rect.left + rect.width / 2}px`;
container.style.transform = 'translateX(-50%)';
container.style.width = `${rect.width - 20}px`;
container.style.height = `${popupHeight}px`;
container.style.margin = '0';
}
document.addEventListener('click', (e) => {
const container = document.querySelector('#emoticonContainer');
if (!container || container.style.display !== 'block') return;
if (e.target.closest('#emoticonContainer .item_list button')) return;
const btnEmo = document.querySelector('#btn_emo');
if (!container.contains(e.target) && e.target !== btnEmo) hideEmoticonContainer();
});
window.addEventListener('resize', positionEmoticonContainer);
window.addEventListener('scroll', positionEmoticonContainer);
function createDynamicArea() {
const chatItemWrap = document.querySelector('#chatbox > div.chatting-item-wrap');
const chatArea = document.querySelector('#chat_area');
if (!chatItemWrap || !chatArea || document.getElementById(AREA_ID)) return;
// --- #sarsa_btn 제거 ---
const sarsaBtn = document.querySelector('#sarsa_btn');
if (sarsaBtn) sarsaBtn.remove();
const dynamicArea = document.createElement('div');
dynamicArea.id = AREA_ID;
dynamicArea.style.backgroundColor = '#1C1E22';
dynamicArea.style.height = DEFAULT_DYNAMIC_HEIGHT + 'px';
dynamicArea.style.overflow = 'hidden';
dynamicArea.style.boxSizing = 'border-box';
dynamicArea.style.marginTop = '10px';
const toggleBar = document.createElement('div');
toggleBar.id = TOGGLEBAR_ID;
toggleBar.style.height = '20px';
toggleBar.style.backgroundColor = '#232529';
toggleBar.style.cursor = 'ns-resize';
toggleBar.style.userSelect = 'none';
toggleBar.style.display = 'flex';
toggleBar.style.alignItems = 'center';
toggleBar.style.justifyContent = 'center';
dynamicArea.appendChild(toggleBar);
const bar = document.createElement('div');
bar.style.width = '40px';
bar.style.height = '5px';
bar.style.background = 'linear-gradient(145deg, #5c5f63, #1a1c1f)';
bar.style.boxShadow = 'inset 0 1px 1px rgba(255,255,255,0.2), 0 2px 4px rgba(0,0,0,0.5)';
bar.style.borderRadius = '2px';
toggleBar.appendChild(bar);
const content = document.createElement('div');
content.style.height = 'calc(100% - 20px)';
content.style.overflowY = 'auto';
content.style.padding = '12px 5px';
content.style.display = 'flex';
content.style.flexWrap = 'wrap';
content.style.justifyContent = 'center';
content.style.alignContent = 'flex-start';
content.style.rowGap = '10px';
content.style.columnGap = '10px';
dynamicArea.appendChild(content);
chatItemWrap.appendChild(dynamicArea);
let favEmoticons = JSON.parse(localStorage.getItem(FAVORITE_KEY) || '[]');
function renderFavEmoticons() {
content.innerHTML = '';
favEmoticons.forEach((item, index) => {
const img = document.createElement('img');
img.src = item.src;
img.title = item.title;
img.style.width = '22px';
img.style.height = '22px';
img.style.cursor = 'pointer';
img.draggable = true;
img.dataset.index = index;
img.addEventListener('click', e => {
if (e.ctrlKey || e.metaKey) {
favEmoticons.splice(index, 1);
localStorage.setItem(FAVORITE_KEY, JSON.stringify(favEmoticons));
renderFavEmoticons();
e.preventDefault();
e.stopPropagation();
return;
}
syncContainerToDynamic(img.src);
const btn = document.querySelector(`#common_emoticon img[src="${item.src}"]`);
if (btn) btn.parentElement.click();
});
img.addEventListener('dragstart', e => { e.dataTransfer.setData('text/plain', ''); img.classList.add('dragging'); });
img.addEventListener('dragend', e => { img.classList.remove('dragging'); });
content.appendChild(img);
});
content.addEventListener('dragover', e => e.preventDefault());
content.addEventListener('drop', e => {
e.preventDefault();
const dragging = content.querySelector('.dragging');
if (!dragging) return;
const dragIndex = parseInt(dragging.dataset.index);
const dropX = e.clientX;
const dropY = e.clientY;
const imgs = Array.from(content.querySelectorAll('img')).filter(img => img !== dragging);
let closestIndex = favEmoticons.length - 1;
let minDistance = Infinity;
imgs.forEach(img => {
const rect = img.getBoundingClientRect();
const cx = rect.left + rect.width / 2;
const cy = rect.top + rect.height / 2;
const dist = Math.hypot(dropX - cx, dropY - cy);
if (dist < minDistance) {
minDistance = dist;
closestIndex = parseInt(img.dataset.index);
if (dropY < cy) closestIndex -= 0.5;
}
});
const movedItem = favEmoticons.splice(dragIndex, 1)[0];
if (closestIndex % 1 === 0.5) closestIndex = Math.floor(closestIndex);
favEmoticons.splice(closestIndex, 0, movedItem);
localStorage.setItem(FAVORITE_KEY, JSON.stringify(favEmoticons));
renderFavEmoticons();
});
}
renderFavEmoticons();
function setDynamicHeight(newHeight) {
const firstEmo = content.querySelector('img');
const toggleHeight = toggleBar.offsetHeight;
let minHeightDynamic = MIN_HEIGHT;
if (firstEmo) minHeightDynamic = firstEmo.offsetHeight + toggleHeight + 29;
newHeight = Math.max(minHeightDynamic, Math.min(newHeight, MAX_HEIGHT));
dynamicArea.style.height = newHeight + 'px';
chatArea.style.height = (chatItemWrap.offsetHeight - newHeight) + 'px';
content.style.height = (newHeight <= toggleHeight) ? '0' : `calc(100% - ${toggleHeight}px)`;
localStorage.setItem(HEIGHT_KEY, newHeight);
}
const cachedHeight = parseInt(localStorage.getItem(HEIGHT_KEY));
if (!isNaN(cachedHeight)) setDynamicHeight(cachedHeight);
else setDynamicHeight(DEFAULT_DYNAMIC_HEIGHT);
let isDragging = false, startY, startHeight;
toggleBar.addEventListener('mousedown', e => { isDragging = true; startY = e.clientY; startHeight = dynamicArea.offsetHeight; e.preventDefault(); });
window.addEventListener('mousemove', e => { if (!isDragging) return; setDynamicHeight(startHeight + (startY - e.clientY)); });
window.addEventListener('mouseup', () => { isDragging = false; });
const resizeObserver = new ResizeObserver(() => { chatArea.style.height = (chatItemWrap.offsetHeight - dynamicArea.offsetHeight) + 'px'; });
resizeObserver.observe(chatItemWrap);
function syncContainerToDynamic(src) {
if (!container) return;
const allButtons = container.querySelectorAll('#emoticonBox .tab_area li');
allButtons.forEach(btnLi => btnLi.classList.remove('on'));
const matchBtn = Array.from(allButtons).find(btnLi => {
const img = btnLi.querySelector('img');
return img && src.includes(img.src.split('/').pop());
});
if (matchBtn) matchBtn.classList.add('on');
}
document.addEventListener('click', e => {
const target = e.target.closest('#common_emoticon span a img');
if (!target) return;
if (e.ctrlKey || e.metaKey) {
e.preventDefault();
e.stopImmediatePropagation();
const exists = favEmoticons.find(f => f.src === target.src);
if (!exists) {
favEmoticons.push({src: target.src, title: target.title});
localStorage.setItem(FAVORITE_KEY, JSON.stringify(favEmoticons));
renderFavEmoticons();
}
}
}, true);
}
const observer = new MutationObserver((mutations, obs) => {
if (document.querySelector('#chatbox > div.chatting-item-wrap') &&
document.querySelector('#chat_area')) {
createDynamicArea();
obs.disconnect();
}
});
const chatArea = document.querySelector('#chat_area');
if (chatArea) chatArea.style.padding = '3px 18px';
// --- 채팅 입력란 이미지 24px 미리보기 적용 ---
(function() {
const writeArea = document.querySelector('#write_area');
if (!writeArea) return;
const style = document.createElement('style');
style.textContent = `#write_area img { width: 24px !important; height: auto !important; }`;
document.head.appendChild(style);
const chatObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'IMG') {
node.style.width = '24px';
node.style.height = 'auto';
}
});
});
});
chatObserver.observe(writeArea, { childList: true });
})();
observer.observe(document.body, { childList: true, subtree: true });
})();