您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
팸코 뎃글콘을 쉽게 쓸수 있게 해줍니다 이제 뎃글콘을 본문에서도 쓸수 있습니다
// ==UserScript== // @name 팸코 뎃글콘 + 본문콘 // @namespace Violentmonkey Scripts // @match *://*.fmkorea.com/* // @grant none // @version 1.4 // @author - // @description 팸코 뎃글콘을 쉽게 쓸수 있게 해줍니다 이제 뎃글콘을 본문에서도 쓸수 있습니다 // ==/UserScript== (function () { 'use strict'; // 상수 정의 const CONSTANTS = { SPREADSHEET_ID: 'YOUR_SPREADSHEET_ID', SHEET_CONFIG: [ { name: 'Sheet1', displayName: '이름없음' }, { name: 'Sheet2', displayName: '이름없음' }, { name: 'Sheet3', displayName: '이름없음' }, { name: 'Sheet4', displayName: '이름없음' }, { name: 'Sheet5', displayName: '이름없음' }, ], GRID_COLORS: ['#f0f0f0', '#e8f5e9', '#e3f2fd', '#fff3e0', '#fce4ec'], CELL_SIZE: 50, GRID_COLS: 10 }; // 로컬 스토리지 관리를 위한 유틸리티 const storageUtils = { saveRecentEmoticon(url) { try { const recent = JSON.parse(localStorage.getItem('recentEmoticons') || '[]'); const index = recent.indexOf(url); if (index > -1) { recent.splice(index, 1); } recent.unshift(url); localStorage.setItem('recentEmoticons', JSON.stringify(recent.slice(0, 20))); } catch (error) { console.error('Failed to save recent emoticon:', error); } }, getRecentEmoticons() { try { return JSON.parse(localStorage.getItem('recentEmoticons') || '[]'); } catch (error) { console.error('Failed to get recent emoticons:', error); return []; } } }; // 유틸리티 함수들 const utils = { createElementWithStyles: (tag, styles = {}) => { const element = document.createElement(tag); Object.assign(element.style, styles); return element; }, addHideScrollbarStyles: (element) => { Object.assign(element.style, { msOverflowStyle: 'none', scrollbarWidth: 'none' }); element.style.cssText += '::-webkit-scrollbar { display: none; }'; return element; }, debounce: (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } }; // 스프레드시트 데이터 fetcher class SpreadsheetDataFetcher { static async fetchData(spreadsheetId, sheetName) { const url = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/gviz/tq?tqx=out:json&sheet=${sheetName}`; try { const response = await fetch(url); const text = await response.text(); const jsonData = JSON.parse(text.substring(47).slice(0, -2)); return jsonData.table.rows .filter(row => row.c && row.c[0] && row.c[0].v) .map(row => row.c[0].v); } catch (error) { console.error(`Failed to fetch ${sheetName} data:`, error); return []; } } } // 미디어 셀 컴포넌트 class MediaCell { constructor(url, index, cols) { this.url = url; this.index = index; this.cols = cols; this.element = this.createElement(); } createElement() { const cell = utils.createElementWithStyles('div', { width: `${CONSTANTS.CELL_SIZE}px`, height: `${CONSTANTS.CELL_SIZE}px`, backgroundColor: CONSTANTS.GRID_COLORS[Math.floor(this.index / this.cols) % CONSTANTS.GRID_COLORS.length], border: '1px solid #ccc', display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column', position: 'relative', transition: 'all 0.3s ease', zIndex: '1' }); const mediaElement = this.createImageElement(); cell.appendChild(mediaElement); this.addEventListeners(cell); return cell; } createImageElement() { const img = utils.createElementWithStyles('img', { width: '100%', height: '100%', objectFit: 'cover', position: 'absolute', top: '0', left: '0', transition: 'all 0.3s ease' }); img.src = this.url; img.alt = '이미지'; img.onerror = () => this.handleMediaError(img); return img; } handleMediaError(mediaElement) { mediaElement.style.display = 'none'; const errorText = utils.createElementWithStyles('div', { fontSize: '10px', color: '#999' }); errorText.textContent = '이미지 없음'; this.element.appendChild(errorText); } addEventListeners(cell) { cell.addEventListener('mouseover', () => this.handleMouseOver(cell)); cell.addEventListener('mouseout', () => this.handleMouseOut(cell)); cell.addEventListener('click', () => this.handleClick()); } handleMouseOver(cell) { Object.assign(cell.style, { backgroundColor: '#e0e0e0', cursor: 'pointer', opacity: '0.9', transform: 'scale(2)', zIndex: '2' }); } handleMouseOut(cell) { Object.assign(cell.style, { backgroundColor: CONSTANTS.GRID_COLORS[Math.floor(this.index / this.cols) % CONSTANTS.GRID_COLORS.length], opacity: '1', transform: 'scale(1)', zIndex: '1' }); } handleClick() { storageUtils.saveRecentEmoticon(this.url); // XpressEditor 처리 const xpressEditor = document.querySelector('.xpress-editor'); if (xpressEditor) { const editorFrame = xpressEditor.querySelector('iframe'); if (editorFrame && editorFrame.contentWindow) { const doc = editorFrame.contentWindow.document; const bodyElement = doc.getElementById('___body'); if (bodyElement) { const mediaTag = `<img src="${this.url}" style="max-width: 100%;" />`; // 현재 선택 영역 가져오기 const selection = editorFrame.contentWindow.getSelection(); if (selection.rangeCount > 0) { // 현재 커서 위치의 range 가져오기 const range = selection.getRangeAt(0); // 새로운 컨텐츠 생성 const newContent = doc.createElement('div'); newContent.innerHTML = `<p>${mediaTag}</p><p><br /></p>`; // 새 컨텐츠의 노드들을 순서대로 삽입 Array.from(newContent.childNodes).forEach(node => { range.insertNode(node); range.setStartAfter(node); }); } else { // 선택 영역이 없는 경우 본문 끝에 추가 const newContent = doc.createElement('div'); newContent.innerHTML = `<p>${mediaTag}</p><p><br /></p>`; bodyElement.append(...newContent.childNodes); } } } } // 기존 댓글창 처리 const textarea = document.querySelector('.fdb_lst_ul #re_cmt textarea') || document.querySelector('.cmt_editor textarea'); if (textarea) { textarea.value = this.url; textarea.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); if (textarea.hasAttribute('data-autogrow')) { textarea.style.height = 'auto'; textarea.style.height = `${textarea.scrollHeight}px`; } setTimeout(() => { const submitButton = document.querySelector('input.bd_btn.keyup_alt'); if (submitButton) { submitButton.click(); } }, 100); } // 모달 닫기 const modal = document.querySelector('.emotion-modal'); if (modal) { modal.style.display = 'none'; } } } // 모달 컴포넌트 class EmotionModal { constructor() { this.modal = this.createModal(); this.modalContent = this.createModalContent(); this.button = this.createButton(); this.initialize(); this.initializeXpressEditorButton(); } async initialize() { try { const gridData = await this.fetchAllSheetData(); if (gridData.length === 0) { console.log('No images to display'); return; } this.setupModalContent(gridData); this.setupEventListeners(); this.updateButtonPosition(); document.body.appendChild(this.button); document.body.appendChild(this.modal); } catch (error) { console.error('Failed to initialize emotion modal:', error); } } async fetchAllSheetData() { const gridData = await Promise.all( CONSTANTS.SHEET_CONFIG.map(sheet => SpreadsheetDataFetcher.fetchData(CONSTANTS.SPREADSHEET_ID, sheet.name) ) ); return CONSTANTS.SHEET_CONFIG .map((config, index) => ({ data: gridData[index], config: config })) .filter(item => item.data.length > 0); } createModal() { const modal = utils.addHideScrollbarStyles( utils.createElementWithStyles('div', { display: 'none', position: 'fixed', zIndex: '1000', left: '0', top: '0', width: '100%', height: '100%', backgroundColor: 'rgba(0,0,0,0.4)', overflowY: 'scroll' }) ); modal.className = 'emotion-modal'; return modal; } createModalContent() { return utils.addHideScrollbarStyles( utils.createElementWithStyles('div', { backgroundColor: '#fefefe', margin: '5% auto', marginBottom: '5%', padding: '15px', border: '1px solid #888', width: '80%', maxWidth: '600px', borderRadius: '5px', position: 'relative', maxHeight: '90vh', overflowY: 'scroll' }) ); } createButton() { const button = utils.createElementWithStyles('button', { position: 'fixed', zIndex: '1000', padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', boxShadow: '0 4px 6px rgba(0,0,0,0.1)', transition: 'all 0.3s ease' }); button.textContent = '댓글콘'; return button; } // Xpress 에디터 버튼 초기화 initializeXpressEditorButton() { const editor = document.querySelector('.xpress-editor'); if (editor) { const toolDiv = editor.querySelector('.tool'); if (toolDiv) { const eighthChild = toolDiv.children[7]; if (eighthChild) { const newUl = document.createElement('ul'); const newLi = document.createElement('li'); const newSpan = document.createElement('span'); newSpan.textContent = '댓글콘'; newSpan.style.height = '21px'; newSpan.style.width = '60px'; newSpan.style.display = 'inline-block'; newSpan.style.cursor = 'pointer'; // 클릭 이벤트 추가 newSpan.addEventListener('click', () => { this.modal.style.display = 'block'; }); newLi.appendChild(newSpan); newUl.appendChild(newLi); eighthChild.after(newUl); } } } } setupModalContent(gridData) { // 상단 헤더 컨테이너 생성 const headerContainer = utils.createElementWithStyles('div', { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', marginBottom: '15px' }); // 제목 생성 const title = utils.createElementWithStyles('h3', { margin: '0', padding: '0' }); title.textContent = '댓글콘'; // 스프레드시트 링크 생성 const spreadsheetLink = utils.createElementWithStyles('a', { display: 'flex', alignItems: 'center', padding: '5px 10px', backgroundColor: '#34A853', color: 'white', borderRadius: '4px', textDecoration: 'none', fontSize: '12px', cursor: 'pointer', transition: 'background-color 0.2s' }); // 구글 시트 아이콘 SVG const sheetIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" style="margin-right: 5px;" fill="white"> <path d="M19 3H5C3.89 3 3 3.89 3 5V19C3 20.11 3.89 21 5 21H19C20.11 21 21 20.11 21 19V5C21 3.89 20.11 3 19 3M19 19H5V5H19V19M17 17H7V7H17V17M15 15H9V13H15V15M15 11H9V9H15V11Z"/> </svg> `; spreadsheetLink.innerHTML = `${sheetIcon} 시트 바로가기`; spreadsheetLink.href = `https://docs.google.com/spreadsheets/d/${CONSTANTS.SPREADSHEET_ID}`; spreadsheetLink.target = '_blank'; spreadsheetLink.rel = 'noopener noreferrer'; // 마우스 오버 효과 spreadsheetLink.addEventListener('mouseover', () => { spreadsheetLink.style.backgroundColor = '#2E9548'; }); spreadsheetLink.addEventListener('mouseout', () => { spreadsheetLink.style.backgroundColor = '#34A853'; }); // 헤더에 요소들 추가 headerContainer.appendChild(title); headerContainer.appendChild(spreadsheetLink); const mainContainer = this.createMainContainer(); // 최근 사용 섹션 추가 const recentEmoticons = storageUtils.getRecentEmoticons(); if (recentEmoticons.length > 0) { const recentSection = { data: recentEmoticons, config: { displayName: '최근 사용' } }; mainContainer.appendChild(this.createGridSection(recentSection)); mainContainer.appendChild(this.createDivider()); } // 기존 그리드 데이터 추가 gridData.forEach((item, index) => { if (index > 0) { mainContainer.appendChild(this.createDivider()); } mainContainer.appendChild(this.createGridSection(item)); }); this.modalContent.innerHTML = ''; this.modalContent.appendChild(headerContainer); this.modalContent.appendChild(mainContainer); this.modal.appendChild(this.modalContent); } createMainContainer() { return utils.addHideScrollbarStyles( utils.createElementWithStyles('div', { display: 'flex', flexDirection: 'column', gap: '15px', alignItems: 'center', width: '100%', overflowX: 'hidden', overflowY: 'scroll' }) ); } createDivider() { return utils.createElementWithStyles('hr', { width: '90%', margin: '8px auto' }); } createGridSection(item) { const section = utils.createElementWithStyles('div', { width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }); const label = this.createSectionLabel(item); const grid = this.createGrid(item.data); section.appendChild(label); section.appendChild(grid); return section; } createSectionLabel(item) { const label = utils.createElementWithStyles('div', { fontSize: '12px', fontWeight: 'bold', padding: '4px', backgroundColor: item.config.displayName === '최근 사용' ? '#e3f2fd' : '#f5f5f5', borderRadius: '3px', marginBottom: '8px', width: '100%', textAlign: 'center' }); label.textContent = item.config.displayName === '최근 사용' ? '최근 사용' : `${item.config.displayName} (${item.data.length}개)`; return label; } createGrid(contents) { const grid = utils.createElementWithStyles('div', { display: 'grid', gridTemplateColumns: `repeat(${CONSTANTS.GRID_COLS}, ${CONSTANTS.CELL_SIZE}px)`, gap: '3px', justifyContent: 'center', padding: '8px', transition: 'all 0.3s ease' }); contents.forEach((url, index) => { const cell = new MediaCell(url, index, CONSTANTS.GRID_COLS); grid.appendChild(cell.element); }); return grid; } setupEventListeners() { // 기존 이벤트 리스너들 this.button.addEventListener('click', () => this.modal.style.display = 'block'); this.modal.addEventListener('click', (event) => { if (event.target === this.modal) { this.modal.style.display = 'none'; } }); // 스프레드시트 단축키 추가 document.addEventListener('keydown', (e) => { if (e.altKey && e.key.toLowerCase() === 's') { e.preventDefault(); // 기본 동작 방지 window.open(`https://docs.google.com/spreadsheets/d/${CONSTANTS.SPREADSHEET_ID}`, '_blank'); } }); const debouncedUpdatePosition = utils.debounce(() => this.updateButtonPosition(), 100); window.addEventListener('scroll', debouncedUpdatePosition); window.addEventListener('resize', debouncedUpdatePosition); this.observeDOMChanges(); } updateButtonPosition() { const content = document.querySelector('.rd_nav.img_tx.fr.m_btn_wrp'); if (!content) { console.error('Navigation element not found'); return; } const contentRect = content.getBoundingClientRect(); const windowHeight = window.innerHeight; if (contentRect.bottom <= windowHeight) { this.button.style.top = `${contentRect.bottom - 140}px`; this.button.style.right = `${window.innerWidth - contentRect.right - 66}px`; } } observeDOMChanges() { const observer = new MutationObserver(() => { const commentSection = document.querySelector('.rd_ft_nav.clear'); this.button.style.display = commentSection ? 'block' : 'none'; if (commentSection) { this.updateButtonPosition(); } }); observer.observe(document.body, { childList: true, subtree: true }); } } // 애플리케이션 초기화 function initializeApp() { const commentSection = document.querySelector('.rd_ft_nav.clear'); if (commentSection) { new EmotionModal(); } else { console.log('Comment section not found. Button will be created only if XpressEditor exists.'); const xpressEditor = document.querySelector('.xpress-editor'); if (xpressEditor) { new EmotionModal(); } } } // 핫키 설정 document.addEventListener('keydown', (e) => { // Alt + E로 이모티콘 모달 열기 if (e.altKey && e.key.toLowerCase() === 'e') { const modal = document.querySelector('.emotion-modal'); if (modal) { modal.style.display = modal.style.display === 'none' ? 'block' : 'none'; } } }); // 스크립트 실행 (() => { // DOM이 완전히 로드된 후 실행 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initializeApp); } else { initializeApp(); } })(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址