Greasy Fork is available in English.

Zod.kr 편의성 스크립트

서명 확장 등 편의 기능 포함, 단축키로 페이지 이동하기 등

// ==UserScript==
// @name         Zod.kr 편의성 스크립트
// @namespace    http://tampermonkey.net/
// @version      1.33
// @description  서명 확장 등 편의 기능 포함, 단축키로 페이지 이동하기 등
// @match        https://zod.kr/*
// @match        https://*.zod.kr/*
// @grant        unsafeWindow
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 1) jQuery 로드 대기
    function waitForjQuery(callback) {
        if (typeof unsafeWindow.jQuery === 'undefined') {
            setTimeout(function() {
                waitForjQuery(callback);
            }, 100);
        } else {
            callback(unsafeWindow.jQuery);
        }
    }

    function main($) {
        $(document).ready(function() {
            // ---------------------------------
            // 0. 서명 확장/축소
            // ---------------------------------
            let signaturesExpanded = false;
            let expandButtons = [];

            $('.app-article-signature__profile-body').each(function() {
                var signature = $(this);
                var contentDiv = signature.find('div[style*="max-height:100px"]');

                if (contentDiv.length) {
                    const expandButton = $('<button>서명 확장</button>');
                    expandButton.css({
                        position: 'relative',
                        backgroundColor: '#3F9DFF',
                        color: 'white',
                        border: 'none',
                        borderRadius: '5px',
                        cursor: 'pointer',
                        padding: '5px 10px',
                        marginTop: '10px',
                        zIndex: '1000'
                    });

                    expandButton.on('click', function() {
                        if (signaturesExpanded) {
                            contentDiv.css({
                                'max-height': '100px',
                                'height': '',
                                'overflow': 'hidden'
                            });
                            expandButton.text('서명 확장');
                            signaturesExpanded = false;
                        } else {
                            contentDiv.css({
                                'max-height': '100%',
                                'height': 'auto',
                                'overflow': 'visible'
                            });
                            expandButton.text('서명 축소');
                            signaturesExpanded = true;
                        }
                    });

                    signature.append(expandButton);
                    expandButtons.push({ button: expandButton, contentDiv: contentDiv });
                }
            });

            // ---------------------------------
            // 1. 여러 단축키(이동)
            // ---------------------------------
            let keyDownTimes = {};
            const navigationKeys = {
                'z': 'https://zod.kr/', // ZOD 메인화면
                'a': 'https://zod.kr/all', // 전체글보기

                'n': 'https://zod.kr/news_all', // 뉴스 모아보기 게시판

                'r': 'https://zod.kr/review', // 리뷰 모아보기 게시판
                'b': 'https://zod.kr/benchmark', // 리뷰 > 벤치마크 게시판

                'c': 'https://zod.kr/community', // 커뮤니티 모아보기 게시판
                'f': 'https://zod.kr/free', // 커뮤니티 > 자유게시판
                'g': 'https://zod.kr/game', // 커뮤니티 > 게임게시판

                'h': 'https://zod.kr/hardware', // PC하드웨어 모아보기 게시판
                '1': 'https://zod.kr/cpu', // PC하드웨어 > CPU / 메인보드 / 램
                '2': 'https://zod.kr/gpu', // PC하드웨어 > 그래픽카드
                '3': 'https://zod.kr/case', // PC하드웨어 > 케이스 / 쿨링
                '4': 'https://zod.kr/ssd', // PC하드웨어 > 저장장치
                '5': 'https://zod.kr/psu', // PC하드웨어 > 파워서플라이
                '6': 'https://zod.kr/display', // PC하드웨어 > 디스플레이
                '7': 'https://zod.kr/keyma', // PC하드웨어 > 키보드 / 마우스
                '8': 'https://zod.kr/audio', // PC하드웨어 > 오디오
                '9': 'https://zod.kr/general', // PC하드웨어 > PC 일반
                '0': 'https://zod.kr/pcbuild', // PC하드웨어 > 조립 / 견적

                'm': 'https://zod.kr/device', // 모바일 모아보기 게시판
                't': 'https://zod.kr/all_tips', // 정보 모아보기 게시판
                'u': 'https://zod.kr/user_review', // 정보 > 유저리뷰 게시판
                'd': 'https://zod.kr/deal', // 특가 모아보기 게시판

                'q': 'https://zod.kr/qna', // 문의/버그신고 게시판
                '`': 'https://zod.kr/member/notifications', // 내 알림 목록 보기
                'x': 'https://zod.kr/notice', // 공지사항 게시판

                '\\': 'https://zod.kr/popular'

            };

            // ---------------------------------
            // 2. 검색창 관련
            // ---------------------------------
            // 2-1) (모바일) S 로 여는 검색
            const mobileSearchBtn = document.querySelector('.app-board-container--only-mobile .app-icon-button');
            const MOBILE_SEARCH_INPUT_SELECTOR = 'input[name="search_keyword"].app-input.app-input-expand';

            // 2-2) (오버레이) Alt+S / Ctrl+Alt+S 로 여는 검색
            const overlaySearchToggleBtn = document.querySelector('a.app-header-item.app-icon-button.app-icon-button-gray.app-search-toggle');
            const OVERLAY_SEARCH_INPUT_SELECTOR = 'input.app-search-form__input[name="search_keyword"]';

            // ---------------------------------
            // 3. keydown 핸들러
            // ---------------------------------
            $(document).on('keydown', function(e) {
                const key = e.key.toLowerCase();

                // (A) 입력창 포커스 중인 경우 => ESC 나 Alt+S 만 예외 통과
                if ($(':focus').is('input, textarea, [contenteditable="true"]')) {
                    // 1) ESC 키
                    if (e.key === 'Escape') {
                        // 여기서 검색창 닫기 로직 등 처리
                        const closeButtonSmall = document.querySelector('.app-dialog-close');
                        if (closeButtonSmall && closeButtonSmall.offsetHeight > 0) {
                            closeButtonSmall.click();
                            return;
                        }
                        if (overlaySearchToggleBtn) {
                            overlaySearchToggleBtn.click();
                            setTimeout(() => {
                                const overlaySearchInput = document.querySelector(OVERLAY_SEARCH_INPUT_SELECTOR);
                                if (overlaySearchInput) {
                                    overlaySearchInput.focus();
                                }
                            }, 100);
                        }
                        return;
                    }

                    // 2) Alt+S 키
                    if (key === 's' && e.altKey) {
                        e.preventDefault();
                        if (overlaySearchToggleBtn) {
                            overlaySearchToggleBtn.click();
                            setTimeout(() => {
                                const overlaySearchInput = document.querySelector(OVERLAY_SEARCH_INPUT_SELECTOR);
                                if (overlaySearchInput) {
                                    overlaySearchInput.focus();
                                }
                            }, 100);
                        }
                        return;
                    }

                    // 그 외에는 단축키 동작 막고 그냥 return
                    return;
                }

                // (B) ESC - 이제 "열려 있다면 무조건 닫기" 방식
                if (e.key === 'Escape') {
                    // -- 1) 'S'로 열렸을 때 닫기 (모바일 검색창)
                    const closeButtonSmall = document.querySelector('.app-dialog-close');
                    if (closeButtonSmall && closeButtonSmall.offsetHeight > 0) {
                        closeButtonSmall.click();
                        return;
                    }

                    // -- 2) 'Alt+S'/'Ctrl+Alt+S' 로 열렸을 때 닫기 (전체 검색창)
                    if (overlaySearchToggleBtn) {
                        overlaySearchToggleBtn.click();
                        // 토글 열림 후 포커스
                        setTimeout(() => {
                            const overlaySearchInput = document.querySelector(OVERLAY_SEARCH_INPUT_SELECTOR);
                            if (overlaySearchInput) {
                                overlaySearchInput.focus();
                            }
                        }, 100);
                    }

                    // (그 외 열려있는 것이 없다면 그냥 끝)
                    return;
                }

                // (C) Alt+S / Ctrl+Alt+S => 통합검색
                if (key === 's' && e.altKey) {
                    e.preventDefault(); // 충돌 방지
                    if (overlaySearchToggleBtn) {
                        overlaySearchToggleBtn.click();
                        setTimeout(() => {
                            const overlaySearchInput = document.querySelector(OVERLAY_SEARCH_INPUT_SELECTOR);
                            if (overlaySearchInput) {
                                // ★ 여기서 검색어 지우기
                                overlaySearchInput.value = '';
                                overlaySearchInput.focus();
                            }
                        }, 100);
                    }
                    return;
                }

                // (D) 'e'로 서명 전체 확장/축소 (altKey/ctrlKey 없을때)
                if (!e.altKey && !e.ctrlKey && key === 'e') {
                    signaturesExpanded = !signaturesExpanded;
                    expandButtons.forEach(function(item) {
                        if (signaturesExpanded) {
                            item.contentDiv.css({
                                'max-height': '100%',
                                'height': 'auto',
                                'overflow': 'visible'
                            });
                            item.button.text('서명 축소');
                        } else {
                            item.contentDiv.css({
                                'max-height': '100px',
                                'height': '',
                                'overflow': 'hidden'
                            });
                            item.button.text('서명 확장');
                        }
                    });
                }

                // (E) 's' 키 => 모바일 검색 열기 + 포커스
                else if (!e.altKey && !e.ctrlKey && key === 's') {
                    if (mobileSearchBtn) {
                        mobileSearchBtn.click();
                        setTimeout(() => {
                            const mobileSearchInput = document.querySelector(MOBILE_SEARCH_INPUT_SELECTOR);
                            if (mobileSearchInput) {
                                // ★ 여기서 검색어 지우기
                                mobileSearchInput.value = '';
                                mobileSearchInput.focus();
                            }
                        }, 100);
                    }
                }

                // (F) 그 외 지정된 단축키
                else if (!e.altKey && !e.ctrlKey) {
                    if (navigationKeys.hasOwnProperty(key)) {
                        if (!keyDownTimes[key]) {
                            keyDownTimes[key] = Date.now();
                        }
                    }
                }
            });

            // ---------------------------------
            // 4. keyup 핸들러
            // ---------------------------------
            $(document).on('keyup', function(e) {
                // 입력 중이면 패스
                if ($(':focus').is('input, textarea, [contenteditable="true"]')) {
                    return;
                }
                // altKey, ctrlKey 눌린 상태도 패스
                if (e.altKey || e.ctrlKey) {
                    return;
                }

                // 길게 누른 단축키 이동
                const key = e.key.toLowerCase();
                if (navigationKeys.hasOwnProperty(key) && keyDownTimes[key]) {
                    let duration = Date.now() - keyDownTimes[key];
                    if (duration >= 80) {
                        window.location.href = navigationKeys[key];
                    }
                    delete keyDownTimes[key];
                }
            });
        });

        // ---------------------------------
        // 5. Alt+Enter, Ctrl+Enter, Alt+Ctrl+Enter => 등록 / 추천+등록
        // ---------------------------------
        function addAltEnterFeature() {
            function addAltEnterListener(textarea) {
                if (textarea.dataset.altEnterListenerAdded === 'true') {
                    return;
                }
                textarea.dataset.altEnterListenerAdded = 'true';

                textarea.addEventListener('keydown', function(event) {
                    if (
                        (event.key === 'Enter' || event.keyCode === 13) &&
                        (event.altKey || event.ctrlKey)
                    ) {
                        event.preventDefault();

                        var form = textarea.closest('form');
                        if (form) {
                            var submitButtons = form.querySelectorAll('button[type="submit"]');
                            var targetButton = null;

                            if (event.altKey && event.ctrlKey) {
                                // Alt+Ctrl+Enter => "추천+등록"
                                submitButtons.forEach(function(button) {
                                    if (button.textContent.trim() === '추천+등록') {
                                        targetButton = button;
                                    }
                                });
                            } else {
                                // Alt+Enter 또는 Ctrl+Enter => "등록"
                                submitButtons.forEach(function(button) {
                                    if (button.textContent.trim() === '등록') {
                                        targetButton = button;
                                    }
                                });
                            }

                            if (targetButton) {
                                targetButton.click();
                                setTimeout(() => {
                                    textarea.blur();
                                    document.activeElement.blur();
                                }, 100);
                            }
                        }
                    }
                });
            }

            // 초기 로드된 textarea 처리
            var textareas = document.querySelectorAll('textarea.app-textarea');
            textareas.forEach(function(textarea) {
                addAltEnterListener(textarea);
            });

            // 동적으로 추가되는 textarea도 처리
            var altEnterObserver = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === 1) {
                            if (node.matches('textarea.app-textarea')) {
                                addAltEnterListener(node);
                            } else {
                                var innerTextareas = node.querySelectorAll('textarea.app-textarea');
                                innerTextareas.forEach(function(textarea) {
                                    addAltEnterListener(textarea);
                                });
                            }
                        }
                    });
                });
            });

            altEnterObserver.observe(document.body, { childList: true, subtree: true });
        }

        addAltEnterFeature();
    }

    waitForjQuery(function($) {
        main($);
    });
})();