아카라이브 썸네일 이미지, 모두 열기

아카라이브 썸네일 이미지 생성, 모두 열기 생성, 그 외 잡다한 기능..

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name         아카라이브 썸네일 이미지, 모두 열기
// @version      1.47
// @icon         https://www.google.com/s2/favicons?sz=64&domain=arca.live
// @description  아카라이브 썸네일 이미지 생성, 모두 열기 생성, 그 외 잡다한 기능..
// @author       ChatGPT
// @match        https://arca.live/b/*
// @match        https://arca.live/u/scrap_list
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
// @namespace Violentmonkey Scripts
// ==/UserScript==

(function() {
    'use strict';

    var config = {
        buttons: true,
        openAllButton: true,
        downAllButton: false,
        compressFiles: true,
        countImages: false,
        originalImage: false,
        downNumber: false,
        controlButtons: false,
        closeButton: false,
        bookmarkButton: false,
        downButton: false,
        thumbnail: true,
        thumbWidth: 100,
        thumbHeight: 62,
        thumbHover: true,
        thumbBlur: true,
        thumbBlurAmount: 2,
        thumbShadow: false,
        origThumb: false,
        thumbHoverBest: true,
        viewer: true,
        scrapList: true,
        test: false,
        test01: false,
        test02: false,
        test03: false

    };

    function handleSettings() {
        var descriptions = {
            buttons: '상단 버튼 생성',
            openAllButton: '모두 열기 버튼 생성',
            downAllButton: '모든 이미지 다운로드 버튼 생성',
            compressFiles: '모든 이미지를 압축해서 다운로드',
            countImages: '모든 이미지의 총 개수를 구하고 진행률 표시',
            originalImage: '원본 이미지로 다운로드(체크 해제시 webp저장)',
            downNumber: '게시글 번호를 누르면 해당 게시글 이미지 다운로드',
            controlButtons: '하단 우측 조작 버튼 생성',
            closeButton: '창닫기 버튼 생성',
            bookmarkButton: '스크랩 버튼 생성',
            downButton: '다운로드 버튼 생성',
            thumbnail: '프리뷰 이미지로 썸네일 생성',
            thumbWidth: '썸네일 너비',
            thumbHeight: '썸네일 높이',
            thumbHover: '썸네일에 마우스 올리면 프리뷰 이미지 출력',
            thumbBlur: '썸네일 블러 효과',
            thumbBlurAmount: '썸네일 블러 효과의 정도',
            thumbShadow: '썸네일 그림자 효과',
            origThumb: '썸네일 클릭 시 원본 이미지 불러오기(유머 채널 개념글)',
            thumbHoverBest: '썸네일에 마우스 올리면 프리뷰 이미지 출력(유머 채널 개념글)',
            viewer: '게시물 이미지 클릭시 이미지 뷰어로 열기',
            scrapList: '스크랩한 게시글 채널별, 탭별 필터링',
            test: '실험실',
            test01: '프리뷰 이미지를 다른 이미지로 대체(특정 조건의 썸네일)',
            test02: '프리뷰 이미지를 다른 이미지로 대체(사용자 지정 썸네일)',
            test03: '대체한 프리뷰 이미지를 썸네일에도 적용'
        };

        var mainConfigKeys = Object.keys(descriptions).filter(key =>
            !['openAllButton', 'downAllButton', 'closeButton', 'bookmarkButton', 'downButton', 'compressFiles', 'countImages', 'originalImage', 'downNumber', 'thumbWidth', 'thumbHeight', 'thumbHover', 'thumbBlur', 'thumbBlurAmount', 'thumbShadow', 'origThumb', 'thumbHoverBest', 'test01', 'test02', 'test03'].includes(key)
        );

        function saveConfig() {
            for (var key in config) {
                if (config.hasOwnProperty(key)) {
                    GM_setValue(key, config[key]);
                }
            }
        }

        function loadConfig() {
            for (var key in config) {
                if (config.hasOwnProperty(key)) {
                    config[key] = GM_getValue(key, config[key]);
                }
            }
        }

        function createBaseWindow(id, titleText) {
            var existingWindow = document.getElementById(id);
            if (existingWindow) {
                existingWindow.remove();
            }

            var window = document.createElement('div');
            Object.assign(window.style, {
                position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
                width: '250px', padding: '20px', background: '#ffffff',
                border: '1px solid #cccccc', borderRadius: '10px', boxShadow: '0px 0px 10px rgba(0, 0, 0, 0.3)',
                zIndex: '9999', textAlign: 'left', display: 'flex', flexDirection: 'column', alignItems: 'center'
            });
            window.id = id;

            var title = document.createElement('div');
            Object.assign(title.style, { fontSize: '24px', fontWeight: 'bold', marginBottom: '10px' });
            title.innerHTML = titleText;
            window.appendChild(title);

            return window;
        }

        function createConfigInput(key) {
            var configDiv = document.createElement('div');
            Object.assign(configDiv.style, { marginBottom: '5px', display: 'flex', alignItems: 'center' });

            var label = document.createElement('label');
            label.innerHTML = key + ': ';
            Object.assign(label.style, { marginRight: '5px', marginBottom: '3px' });
            label.title = descriptions[key];

            var input = document.createElement('input');
            input.type = (typeof config[key] === 'boolean') ? 'checkbox' : 'text';
            input.value = config[key];
            input.checked = config[key];
            if (input.type === 'text') {
                Object.assign(input.style, { width: '40px', height: '20px', padding: '0 5px' });
            }
            input.addEventListener('input', function(event) {
                config[key] = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
            });

            configDiv.appendChild(label);
            configDiv.appendChild(input);

            if (['buttons', 'downAllButton', 'controlButtons', 'downButton', 'thumbnail', 'test', 'test02'].includes(key)) {
                var settingsIcon = document.createElement('span');
                settingsIcon.innerHTML = '⚙️';
                Object.assign(settingsIcon.style, { cursor: 'pointer', marginLeft: '3px', marginBottom: '2px' });
                settingsIcon.addEventListener('click', function() {
                    var windowFunctions = {
                        buttons: createButtonsWindow,
                        downAllButton: createDownloadWindow,
                        controlButtons: createControlButtonsWindow,
                        downButton: createDownloadWindow,
                        thumbnail: createThumbnailWindow,
                        test: createTestWindow,
                        test02: createFilterWindow
                    };
                    windowFunctions[key] && windowFunctions[key]();
                });
                configDiv.appendChild(settingsIcon);
            }

            return configDiv;
        }

        function createButton(text, color, onClick) {
            var button = document.createElement('button');
            button.innerHTML = text;
            Object.assign(button.style, { border: '1px solid #cccccc', borderRadius: '5px', marginRight: '10px' });
            button.addEventListener('click', onClick);
            button.addEventListener('mouseover', function() {
                button.style.background = color;
                button.style.color = '#ffffff';
            });
            button.addEventListener('mouseout', function() {
                button.style.background = '';
                button.style.color = '#000000';
            });
            return button;
        }

        function createButtonContainer(confirmText, cancelText, onConfirm, onCancel) {
            var buttonContainer = document.createElement('div');
            Object.assign(buttonContainer.style, { display: 'flex', marginTop: '10px' });

            buttonContainer.appendChild(createButton(confirmText, '#007bff', onConfirm));
            buttonContainer.appendChild(createButton(cancelText, '#ff0000', onCancel));

            return buttonContainer;
        }

        function createSettingsWindow() {
            var settingsWindow = createBaseWindow('settingsWindow', 'Settings');

            mainConfigKeys.forEach(function(key) {
                settingsWindow.appendChild(createConfigInput(key));
            });

            var tooltip = document.createElement('div');
            Object.assign(tooltip.style, { fontSize: '12px', marginTop: '5px', marginBottom: '10px', color: 'gray' });
            tooltip.innerHTML = '마우스를 올리면 설명이 나옵니다';
            settingsWindow.appendChild(tooltip);

            settingsWindow.appendChild(createButtonContainer('확인', '취소', function() {
                saveConfig();
                settingsWindow.remove();
                location.reload();
            }, function() {
                settingsWindow.remove();
            }));

            document.body.appendChild(settingsWindow);
        }

        function createSubSettingsWindow(id, title, keys, additionalContent) {
            var subWindow = createBaseWindow(id, title);

            // keys가 있으면 Config 입력들을 추가
            keys.forEach(function(key) {
                subWindow.appendChild(createConfigInput(key));
            });

            // 추가로 받은 내용들을 subWindow에 추가
            if (additionalContent) {
                Object.keys(additionalContent).forEach(function(key) {
                    subWindow.appendChild(additionalContent[key]);
                });
            }

            // 버튼 추가
            subWindow.appendChild(createButtonContainer('확인', '취소', function() {
                saveConfig();
                subWindow.remove();
            }, function() {
                subWindow.remove();
            }));

            document.body.appendChild(subWindow);
        }

        function createButtonsWindow() {
            createSubSettingsWindow('buttonsWindow', 'Buttons', ['openAllButton', 'downAllButton']);
        }

        function createDownloadWindow() {
            createSubSettingsWindow('downloadSettingsWindow', 'Download', ['compressFiles', 'countImages', 'originalImage', 'downNumber']);
        }

        function createControlButtonsWindow() {
            createSubSettingsWindow('controlButtonsWindow', 'Control Buttons', ['closeButton', 'bookmarkButton', 'downButton']);
        }

        function createThumbnailWindow() {
            createSubSettingsWindow('thumbnailWindow', 'Thumbnail', ['thumbWidth', 'thumbHeight', 'thumbHover', 'thumbBlur', 'thumbBlurAmount', 'thumbShadow', 'origThumb', 'thumbHoverBest']);
        }

        function createTestWindow() {
            createSubSettingsWindow('testWindow', 'Test', ['test01', 'test02', 'test03']);
        }

        function createFilterWindow() {
            var savedLinks = GM_getValue('savedLinks', []);

            // 링크 목록 생성 부분
            var linkListContainer = document.createElement('div');
            linkListContainer.id = 'linkListContainer';
            linkListContainer.style.display = 'flex';
            linkListContainer.style.flexWrap = 'wrap';
            linkListContainer.style.justifyContent = 'center';  // 가로 중앙 정렬
            linkListContainer.style.alignItems = 'center';  // 세로 중앙 정렬
            linkListContainer.style.gap = '10px'; // Adds spacing between items
            linkListContainer.style.marginBottom = '10px';

            savedLinks.forEach(function (link, index) {
                var linkDiv = document.createElement('div');
                linkDiv.style.display = 'flex';
                linkDiv.style.flexDirection = 'column';
                linkDiv.style.alignItems = 'center';
                linkDiv.style.marginTop = '10px';
                linkDiv.style.marginBottom = '10px';
                linkDiv.style.width = '60px'; // Ensure all items have a fixed width to align properly

                // 썸네일 이미지
                var thumbnail = document.createElement('img');
                thumbnail.src = link;
                thumbnail.style.width = '50px';
                thumbnail.style.height = '50px';
                thumbnail.style.objectFit = 'cover';
                thumbnail.style.marginBottom = '5px';

                // 이미지 클릭 시 링크 입력창에 설정
                thumbnail.addEventListener('click', function () {
                    var linkInput = document.querySelector('#linkInput');
                    if (linkInput) {
                        linkInput.value = link;
                    }
                });

                // 삭제 버튼
                var deleteButton = document.createElement('button');
                deleteButton.textContent = 'Delete';
                deleteButton.style.border = '1px solid #cccccc';
                deleteButton.style.borderRadius = '5px';
                deleteButton.style.marginTop = '5px';
                deleteButton.style.fontSize = '12px';
                deleteButton.addEventListener('click', function () {
                    savedLinks.splice(index, 1);
                    GM_setValue('savedLinks', savedLinks);
                    createFilterWindow(); // 링크 삭제 후 새로고침
                });

                linkDiv.appendChild(thumbnail);
                linkDiv.appendChild(deleteButton);
                linkListContainer.appendChild(linkDiv);
            });

            // 링크 추가 입력창과 버튼
            var addLinkContainer = document.createElement('div');
            addLinkContainer.style.display = 'flex';
            addLinkContainer.style.alignItems = 'center';

            var linkInput = document.createElement('input');
            linkInput.type = 'text';
            linkInput.id = 'linkInput';
            linkInput.placeholder = '썸네일 링크 입력';
            linkInput.style.flex = '1';
            linkInput.style.marginRight = '5px';
            linkInput.style.padding = '5px';
            linkInput.style.width = '180px';
            linkInput.style.fontSize = '12px';

            var addLinkButton = document.createElement('button');
            addLinkButton.textContent = 'Add';
            addLinkButton.style.border = '1px solid #cccccc';
            addLinkButton.style.borderRadius = '5px';
            addLinkButton.addEventListener('click', function () {
                var newLink = linkInput.value.trim();
                if (newLink && !savedLinks.includes(newLink)) {
                    savedLinks.push(newLink);
                    GM_setValue('savedLinks', savedLinks);
                    createFilterWindow(); // 새 링크 추가 후 새로고침
                }
            });

            addLinkContainer.appendChild(linkInput);
            addLinkContainer.appendChild(addLinkButton);

            // 툴팁
            var tooltip = document.createElement('div');
            Object.assign(tooltip.style, { fontSize: '12px', marginTop: '5px', marginBottom: '10px', color: 'gray' });
            tooltip.innerHTML = '해당 게시글의 다른 이미지로 대체';

            createSubSettingsWindow('filterWindow', 'Filter', [], {
                linkListContainer: linkListContainer,
                addLinkContainer: addLinkContainer,
                tooltip: tooltip
            });
        }

        loadConfig();

        GM_registerMenuCommand('설정', function() {
            createSettingsWindow();
        });
    }

    function arcaLiveScrapList() {
        const header = document.querySelector('.list-table ');

        // 부모 div (전체 컨테이너)
        var containerDiv = document.createElement('div');
        containerDiv.style.marginBottom = '0.5rem';

        // 페이징 요소의 HTML을 가져옵니다.
        const paginationHTML = document.querySelector('.pagination.justify-content-center');
        const paginationDiv = document.createElement('div');
        paginationDiv.innerHTML = paginationHTML.outerHTML;
        paginationDiv.style.marginBottom = '0.5rem';

        // Create filter div (채널과 탭 필터)
        const filterDiv = document.createElement('div');
        filterDiv.className = 'filterDiv';
        filterDiv.style.display = 'flex';

        // Create channel filter
        const channelFilter = document.createElement('select');
        channelFilter.className = 'form-control select-list-type';
        channelFilter.name = 'sort';
        channelFilter.style.cssText = 'width: auto; height: 2rem; float: left; padding: 0 0 0 .5rem; font-size: .9rem;';
        const defaultChannelOption = document.createElement('option');
        defaultChannelOption.value = '';
        defaultChannelOption.text = '채널 선택';
        channelFilter.appendChild(defaultChannelOption);
        filterDiv.appendChild(channelFilter);

        // Create tab filter
        const tabFilter = document.createElement('select');
        tabFilter.className = 'form-control select-list-type';
        tabFilter.name = 'sort';
        tabFilter.style.cssText = 'width: auto; height: 2rem; float: left; padding: 0 0 0 .5rem; font-size: .9rem;';
        const defaultTabOption = document.createElement('option');
        defaultTabOption.value = '';
        defaultTabOption.text = '탭 선택';
        tabFilter.appendChild(defaultTabOption);
        filterDiv.appendChild(tabFilter);

        // gridContainer에 각 영역 추가
        containerDiv.appendChild(paginationDiv);
        containerDiv.appendChild(filterDiv);

        // 문서의 body에 추가 (혹은 다른 부모 요소에 추가 가능)
        header.parentNode.insertBefore(containerDiv, header);

        // Collect channels and tabs
        const posts = document.querySelectorAll('.vrow.column.filtered, .vrow.column');
        const channelTabMap = {};

        posts.forEach(post => {
            const badges = post.querySelectorAll('.badge');
            if (badges.length >= 2) {
                const channel = badges[0].textContent.trim();
                const tab = badges[1].textContent.trim();
                if (!channelTabMap[channel]) {
                    channelTabMap[channel] = new Set();
                }
                channelTabMap[channel].add(tab);
            }
        });

        // Populate channel filter
        Object.keys(channelTabMap).forEach(channel => {
            const option = document.createElement('option');
            option.value = channel;
            option.text = channel;
            channelFilter.appendChild(option);
        });

        // Update tab filter based on selected channel
        function updateTabFilter() {
            const selectedChannel = channelFilter.value;
            tabFilter.innerHTML = '';
            const defaultTabOption = document.createElement('option');
            defaultTabOption.value = '';
            defaultTabOption.text = '탭 선택';
            tabFilter.appendChild(defaultTabOption);

            if (selectedChannel && channelTabMap[selectedChannel]) {
                channelTabMap[selectedChannel].forEach(tab => {
                    const option = document.createElement('option');
                    option.value = tab;
                    option.text = tab;
                    tabFilter.appendChild(option);
                });
            }

            filterPosts();
        }

        // Filter posts based on selected channel and tab
        function filterPosts() {
            const selectedChannel = channelFilter.value;
            const selectedTab = tabFilter.value;

            posts.forEach(post => {
                const badges = post.querySelectorAll('.badge');
                if (badges.length >= 2) {
                    const postChannel = badges[0].textContent.trim();
                    const postTab = badges[1].textContent.trim();
                    if ((selectedChannel === '' || postChannel === selectedChannel) &&
                        (selectedTab === '' || postTab === selectedTab)) {
                        post.style.display = '';
                    } else {
                        post.style.display = 'none';
                    }
                }
            });
        }

        channelFilter.addEventListener('change', updateTabFilter);
        tabFilter.addEventListener('change', filterPosts);
    }

    function arcaLive() {
        // 모두 열기 버튼 생성
        if (config.openAllButton) {
            var openAllButton = document.createElement('a');
            openAllButton.className = 'btn btn-sm btn-primary float-left';
            openAllButton.href = '#';
            openAllButton.innerHTML = '<span class="ion-android-list"></span><span> 모두 열기 </span>';
            openAllButton.style.height = '2rem';

            openAllButton.addEventListener('click', function(event) {
                event.preventDefault();

                // 게시글의 수를 계산
                var posts = document.querySelectorAll('a.vrow.column:not(.notice)');
                var postCount = 0;

                // 필터링된 게시글을 제외한 수를 계산
                posts.forEach(function(element) {
                    var href = element.getAttribute('href');
                    var classes = element.className.split(' ');
                    if (href && !classes.includes('filtered') && !classes.includes('filtered-keyword')) {
                        postCount++;
                    }
                });

                // 게시글 수를 포함한 메시지
                const confirmMessage = `총 ${postCount}개의 게시글을 한 번에 엽니다.\n계속 진행하시겠습니까?`;

                // 확인 메시지 표시
                if (confirm(confirmMessage)) {
                    posts.forEach(function(element) {
                        var href = element.getAttribute('href');
                        var classes = element.className.split(' ');
                        if (href && !classes.includes('filtered') && !classes.includes('filtered-keyword')) {
                            window.open(href, '_blank');
                        }
                    });
                }
            });

            var targetElement = document.querySelector('.form-control.select-list-type');
            targetElement.parentNode.insertBefore(openAllButton, targetElement);
        }

        // 이미지와 동영상 다운로드 버튼 생성
        if (config.downAllButton) {
            async function getTotalImages(urls) {
                let totalImages = 0;
                for (const url of urls) {
                    const response = await fetch(url);
                    const html = await response.text();
                    const doc = new DOMParser().parseFromString(html, "text/html");
                    const imageElements = Array.from(doc.querySelectorAll('.article-body img')).filter(img => !img.classList.contains('arca-emoticon'));
                    const gifVideoElements = doc.querySelectorAll('video[data-orig="gif"][data-originalurl]');
                    totalImages += imageElements.length + gifVideoElements.length;
                }
                return totalImages;
            }

            async function downloadMediaSequentially(urls, totalImages, compressFiles) {
                let totalDownloaded = 0;

                // 프로그레스 바 업데이트 함수
                function updateProgressBar(progress) {
                    const progressBar = document.getElementById('progress-bar');
                    progressBar.style.width = progress + '%';
                    progressBar.innerHTML = progress + '%';
                    if (progress === 100) {
                        setTimeout(() => {
                            progressBar.style.backgroundColor = 'orange'; // 100%일 때 배경색을 주황색으로 변경
                        }, 300); // 잠시 딜레이를 주어 애니메이션 완료 후 색상 변경
                    }
                }

                async function downloadFile(url, index, type, requestUrl, zip, title) {
                    const response = await fetch(url);
                    const blob = await response.blob();
                    const extension = type === 'img' ? (config.originalImage ? url.split('.').pop().split('?')[0].toLowerCase() : 'webp') : 'gif';
                    const numbersFromUrl = requestUrl.match(/\d+/)[0];
                    const fileIndex = index + 1; // Index를 1 증가시킴
                    // const sanitizedTitle = title.replace(/[^a-zA-Z0-9가-힣\s]/g, '_'); // 파일 이름에 사용할 수 있도록 제목을 정제
                    const numberedFileName = compressFiles ? `${title}_${String(fileIndex).padStart(2, '0')}.${extension}` : `${numbersFromUrl}_${String(fileIndex).padStart(2, '0')}.${extension}`;
                    if (zip) {
                        zip.file(numberedFileName, blob);
                    } else {
                        const link = document.createElement('a');
                        link.href = window.URL.createObjectURL(blob);
                        link.download = numberedFileName;
                        link.click();
                    }
                }

                async function processNextUrl() {
                    for (let index = 0; index < urls.length; index++) {
                        const url = urls[index];

                        let zip;
                        if (compressFiles) {
                            zip = new JSZip();
                        }
                        const response = await fetch(url);
                        const html = await response.text();
                        const doc = new DOMParser().parseFromString(html, "text/html");

                        const titleElement = doc.querySelector('.title-row .title');
                        let title = '';
                        if (titleElement) {
                            const textNodes = Array.from(titleElement.childNodes)
                                .filter(node => node.nodeType === Node.TEXT_NODE && node.parentElement === titleElement);
                            if (textNodes.length > 0) {
                                title = textNodes.map(node => node.textContent.trim()).join('');
                            }
                        }

                        // arca-emoticon 클래스를 가진 이미지를 제외하고 선택
                        const mediaElements = Array.from(doc.querySelectorAll('.article-body img, .article-body video[data-orig="gif"]')).filter(media => !media.classList.contains('arca-emoticon'));
                        try {
                            if (mediaElements.length > 0) {
                                for (let i = 0; i < mediaElements.length; i++) {
                                    const media = mediaElements[i];
                                    const mediaType = media.tagName.toLowerCase();
                                    const mediaUrl = mediaType === 'img' ? (config.originalImage ? media.getAttribute('src') + "&type=orig" : media.getAttribute('src')) : media.getAttribute('data-originalurl');
                                    if (mediaUrl) {
                                        await downloadFile(mediaUrl, i, mediaType, url, zip, title);
                                        totalDownloaded++;
                                        if (config.countImages) {
                                            const progress = Math.round((totalDownloaded / totalImages) * 100);
                                            updateProgressBar(progress);
                                        }
                                    }
                                }
                                if (zip) {
                                    const content = await zip.generateAsync({ type: 'blob' });
                                    const numbersFromUrl = url.match(/\d+/)[0];
                                    const zipFileName = `${numbersFromUrl}.zip`;
                                    const zipLink = document.createElement('a');
                                    zipLink.href = window.URL.createObjectURL(content);
                                    zipLink.download = zipFileName;
                                    zipLink.click();
                                }
                            }
                        } catch (error) {
                            console.error("Error downloading media:", error);
                        }
                    }
                }

                await processNextUrl();
            }

            var downloadMediaButton = document.createElement('a');
            downloadMediaButton.className = 'btn btn-sm btn-success float-left';
            downloadMediaButton.href = '#';
            downloadMediaButton.innerHTML = '<span class="ion-android-download"></span><span> 다운로드 </span>';
            downloadMediaButton.style.position = 'relative'; // 상대 위치 지정

            // 프로그레스 바 스타일을 가진 div 엘리먼트 추가
            var progressBar = document.createElement('div');
            progressBar.id = 'progress-bar'; // ID 추가
            progressBar.style.position = 'absolute'; // 절대 위치 지정
            progressBar.style.bottom = '5%';
            progressBar.style.left = '0';
            progressBar.style.width = '0%'; // 초기 너비는 0%
            progressBar.style.height = '10%';
            progressBar.style.backgroundColor = 'yellow'; // 프로그레스 바 색상
            progressBar.style.borderRadius = 'inherit';
            progressBar.style.transition = 'width 0.3s ease-in-out'; // 프로그레스 바 애니메이션
            downloadMediaButton.appendChild(progressBar); // 프로그레스 바를 버튼에 추가

            downloadMediaButton.addEventListener('click', async function(event) {
                event.preventDefault();
                var mediaUrls = []; // 다운로드할 미디어 URL을 저장할 배열
                document.querySelectorAll('a.vrow.column:not(.notice)').forEach(function(element) {
                    var href = element.getAttribute('href');
                    var classes = element.className.split(' ');

                    if (classes.includes('filtered') || classes.includes('filtered-keyword')) {
                        return; // 해당 조건이 맞으면 다음으로 넘어감
                    }

                    if (href) {
                        mediaUrls.push(href); // 미디어 URL을 배열에 추가
                    }
                });
                const mediaUrlsCount = mediaUrls.length;

                if (config.countImages) {
                    const initialMessage = `총 ${mediaUrlsCount}개의 게시글을 찾았습니다.\n모든 게시글의 이미지를 확인하여 총 개수를 계산합니다.\n계산하는 데 시간이 오래 걸릴 수 있습니다.\n(설정에서 변경 가능)`;
                    alert(initialMessage);

                    const totalImages = await getTotalImages(mediaUrls);
                    const confirmMessage = `다운로드해야 할 이미지의 총 개수는 ${totalImages}개입니다.\n계속해서 다운로드 하시겠습니까?`;
                    if (confirm(confirmMessage)) {
                        progressBar.style.width = '0%'; // 초기 너비는 0%
                        progressBar.style.backgroundColor = 'yellow'; // 프로그레스 바 색상
                        await downloadMediaSequentially(mediaUrls, totalImages, config.compressFiles); // config.compressFiles 변수 전달
                    }
                } else {
                    // 프로그레스 바를 사용하지 않을 경우에는 다운로드 여부를 확인하는 창 띄우기
                    const confirmMessage = `총 ${mediaUrlsCount}개의 게시글을 한 번에 다운로드합니다.\n다운로드를 진행하시겠습니까?`;
                    if (confirm(confirmMessage)) {
                        progressBar.style.width = '0%'; // 초기 너비는 0%
                        progressBar.style.backgroundColor = 'yellow'; // 프로그레스 바 색상
                        await downloadMediaSequentially(mediaUrls, 0, config.compressFiles); // config.compressFiles 변수 전달
                        progressBar.style.width = '100%';
                        progressBar.style.backgroundColor = 'orange'; // 100%일 때 배경색을 주황색으로 변경

                    }
                }
            });

            var targetElement = document.querySelector('.form-control.select-list-type');
            targetElement.parentNode.insertBefore(downloadMediaButton, targetElement);
        }

        if (config.downNumber) {
            // document.addEventListener("DOMContentLoaded", function() {
            // });
            document.querySelectorAll('.vrow.column:not(.notice) .vcol.col-id').forEach(function(link) {
                link.addEventListener('click', async function(event) {
                    event.preventDefault();  // 기본 동작 방지
                    link.style.color = 'orange'; // 다운로드 시작 시 노란색으로 변경
                    const parentHref = link.closest('.vrow.column').getAttribute('href');
                    await downloadMediaFromUrl(parentHref, config.compressFiles); // compressFiles 변수 전달
                    link.style.color = 'red'; // 다운로드 완료 시 빨간색으로 변경
                });
            });

            async function downloadMediaFromUrl(url, compressFiles) { // compressFiles 변수 추가
                const response = await fetch(url);
                const html = await response.text();
                const doc = new DOMParser().parseFromString(html, "text/html");
                const mediaElements = Array.from(doc.querySelectorAll('.article-body img, .article-body video[data-orig="gif"]')).filter(media => !media.classList.contains('arca-emoticon'));
                let zip;

                const titleElement = doc.querySelector('.title-row .title');
                let title = '';
                if (titleElement) {
                    const textNodes = Array.from(titleElement.childNodes)
                        .filter(node => node.nodeType === Node.TEXT_NODE && node.parentElement === titleElement);
                    if (textNodes.length > 0) {
                        title = textNodes.map(node => node.textContent.trim()).join('');
                    }
                }

                async function downloadFile(mediaUrl, index, type) {
                    const response = await fetch(mediaUrl);
                    const blob = await response.blob();
                    const extension = type === 'img' ? (config.originalImage ? mediaUrl.split('.').pop().split('?')[0].toLowerCase() : 'webp') : 'gif';
                    const fileIndex = index + 1;
                    const numbersFromUrl = url.match(/\d+/)[0];
                    // const sanitizedTitle = title.replace(/[^a-zA-Z0-9가-힣\s]/g, '_'); // 파일 이름에 사용할 수 있도록 제목을 정제
                    const numberedFileName = compressFiles ? `${title}_${String(fileIndex).padStart(2, '0')}.${extension}` : `${numbersFromUrl}_${String(fileIndex).padStart(2, '0')}.${extension}`;
                    if (compressFiles) {
                        zip.file(numberedFileName, blob);
                    } else {
                        const link = document.createElement('a');
                        link.href = window.URL.createObjectURL(blob);
                        link.download = numberedFileName;
                        link.click();
                    }
                }

                async function processMedia() {
                    for (let i = 0; i < mediaElements.length; i++) {
                        const media = mediaElements[i];
                        const mediaType = media.tagName.toLowerCase();
                        const mediaUrl = mediaType === 'img' ? (config.originalImage ? media.getAttribute('src') + "&type=orig" : media.getAttribute('src')) : media.getAttribute('data-originalurl');
                        if (mediaUrl) {
                            await downloadFile(mediaUrl, i, mediaType);
                        }
                    }
                }

                if (compressFiles) {
                    zip = new JSZip();
                }

                await processMedia();

                if (compressFiles) {
                    const content = await zip.generateAsync({ type: 'blob' });
                    const zipFileName = url.match(/\d+/)[0] + '.zip';
                    const zipLink = document.createElement('a');
                    zipLink.href = window.URL.createObjectURL(content);
                    zipLink.download = zipFileName;
                    zipLink.click();
                }
            }
        }

        if (config.thumbnail) {
            document.addEventListener("DOMContentLoaded", function() {
                function checkBlackEdge(img, callback) {
                    var newImg = new Image();
                    newImg.crossOrigin = 'anonymous';
                    newImg.onload = function() {
                        var canvas = document.createElement('canvas');
                        var ctx = canvas.getContext('2d');
                        canvas.width = newImg.width;
                        canvas.height = newImg.height;
                        ctx.drawImage(newImg, 0, 0, newImg.width, newImg.height);

                        var edgeSize = Math.min(newImg.width, newImg.height) * 0.1;
                        var imgData = ctx.getImageData(0, 0, newImg.width, newImg.height);

                        var totalPixels = 0;
                        var blackPixels = 0;

                        for (var x = 0; x < newImg.width; x++) {
                            for (var y = 0; y < newImg.height; y++) {
                                if (x < edgeSize || x >= newImg.width - edgeSize || y < edgeSize || y >= img.height - edgeSize) {
                                    totalPixels++;
                                    var index = (y * newImg.width + x) * 4;
                                    var pixelData = [
                                        imgData.data[index],     // Red
                                        imgData.data[index + 1], // Green
                                        imgData.data[index + 2]  // Blue
                                    ];
                                    if (pixelData[0] === 0 && pixelData[1] === 0 && pixelData[2] === 0) {
                                        blackPixels++;
                                    }
                                }
                            }
                        }

                        var blackPercentage = blackPixels / totalPixels;
                        if (blackPercentage >= 0.33) {
                            callback(true);
                        } else {
                            callback(false);
                        }
                    };
                    newImg.onerror = function() {
                        // 이미지 로드 실패 시에도 콜백 호출하여 처리
                        callback(false);
                    };
                    newImg.src = img.src + "&type=list"; // newImg.src = img.src + "&type=list";
                }

                function setSecondImg(vrow, img) {
                    var href = vrow.href;

                    fetch(href)
                        .then(response => {
                            if (!response.ok) {
                                throw new Error('Request failed with status ' + response.status);
                            }
                            return response.text();
                        })
                        .then(responseText => {
                            var parser = new DOMParser();
                            var htmlDoc = parser.parseFromString(responseText, "text/html");
                            var contentDiv = htmlDoc.querySelector('div.fr-view.article-content');
                            if (!contentDiv) {
                                return;
                            }

                            var Tags = contentDiv.querySelectorAll('img, video');
                            var firstTag = null;

                            for (var i = 0; i < Tags.length; i++) {
                                firstTag = Tags[i];

                                if (firstTag.style.width == '2px' && firstTag.style.height == '2px') {
                                    break;
                                } else if (firstTag.tagName.toLowerCase() === 'img') {
                                    if (!(img.src.split("?")[0] === firstTag.src.split("?")[0])) {
                                        break;
                                    }
                                } else if (firstTag.tagName.toLowerCase() === 'video') {
                                    break;
                                }
                            }

                            if (!firstTag) {
                                return;
                            }

                            var videoOriginalSrc = firstTag.getAttribute('data-originalurl');
                            var videoOriginalSrcType = firstTag.getAttribute('data-orig');
                            var videoPosterSrc = firstTag.getAttribute('poster');
                            var changeImgUrl = null;
                            if (firstTag.tagName.toLowerCase() === 'img') {
                                changeImgUrl = firstTag.src + "&type=list";
                            } else if (firstTag.tagName.toLowerCase() === 'video') {
                                if (videoOriginalSrc && !videoOriginalSrcType) {
                                    changeImgUrl = videoOriginalSrc + "&type=list";
                                } else if (videoPosterSrc) {
                                    changeImgUrl = videoPosterSrc + "&type=list";
                                } else {
                                    changeImgUrl = img.src;
                                }
                            }

                            if (config.test03) {
                                img.onload = function () {
                                    img.parentNode.style.border = '2px solid pink';
                                    // img.parentNode.style.boxShadow = 'rgb(255 155 155) 2px 2px 2px';
                                };
                                img.src = changeImgUrl;
                            }

                            var previewImg = vrow.querySelector('.vrow-preview img')
                            previewImg.src = changeImgUrl.replace("&type=list", '');
                        })
                        .catch(error => {
                            console.error('Error fetching data:', error);
                        });
                }

                const vrows = document.querySelectorAll('a.vrow.column:not(.notice)')
                vrows.forEach(function(vrow) {
                    var vcolId = vrow.querySelector('.vcol.col-id');
                    var vcolTitle = vrow.querySelector('.vcol.col-title');
                    vcolId.style.margin = '0';
                    vcolId.style.height = 'auto';
                    vcolId.style.display = 'flex';
                    vcolId.style.alignItems = 'center'; // 세로 가운데 정렬
                    vcolId.style.justifyContent = 'center'; // 가로 가운데 정렬

                    var vcolThumb = vrow.querySelector('.vcol.col-thumb');
                    if (!vcolThumb) {
                        vcolThumb = document.createElement('span');
                        vcolThumb.className = 'vcol col-thumb';
                        vcolThumb.style.width = config.thumbWidth + 'px';
                        vcolThumb.style.borderRadius = '3px';

                        vrow.querySelector('.vrow-inner').appendChild(vcolThumb);
                        vcolTitle.parentNode.insertBefore(vcolThumb, vcolTitle);
                    }

                    var vrowPreview = vrow.querySelector('.vrow-preview');

                    // vrowPreview가 존재할 때만 썸네일을 추가하도록 조건 추가
                    if (vrowPreview) {
                        var thumbnailCreated = false;  // 썸네일 생성 여부 플래그
                        function createThumbnail() {
                            if (thumbnailCreated) return;  // 이미 썸네일이 생성되었으면 더 이상 생성하지 않음

                            var previewImg = vrowPreview.querySelector('img');
                            if (!previewImg) return;

                            vrow.style.height = 'auto';
                            vrow.style.paddingTop = '3.75px';
                            vrow.style.paddingBottom = '3.75px';
                            vcolThumb.style.height = config.thumbHeight + 'px';

                            var thumbImg = vcolThumb.querySelector('img');
                            if (!thumbImg) {
                                thumbImg = document.createElement('img');
                                thumbImg.src = previewImg.src;
                                thumbImg.style.width = '100%';
                                thumbImg.style.height = '100%';
                                thumbImg.style.objectFit = 'cover';
                                if (config.thumbShadow) {
                                    thumbImg.onload = function () {
                                        vcolThumb.style.boxShadow = 'rgba(0, 0, 0, 0.4) 2px 2px 2px';
                                    }
                                }

                                if (config.test) {
                                    if (config.test01) {
                                        checkBlackEdge(thumbImg, function(hasBlackEdge) {
                                            if (hasBlackEdge) {
                                                setSecondImg(vrow, thumbImg);
                                            }
                                        });
                                    }

                                    if (config.test02) {
                                        function removeQueryString(url) {
                                            var parsedUrl = new URL(url);
                                            return parsedUrl.origin + parsedUrl.pathname;
                                        }

                                        var savedLinks = GM_getValue('savedLinks', []);
                                        var cleanSrc = removeQueryString(thumbImg.src);
                                        if (savedLinks.some(link => cleanSrc.includes(removeQueryString(link)))) {
                                            setSecondImg(vrow, thumbImg);
                                            console.log("Filtered Image:", vcolId.querySelector('span').textContent, thumbImg.src);
                                        }
                                    }
                                }

                                if (config.thumbBlur) {
                                    thumbImg.style.filter = 'blur(' + config.thumbBlurAmount + 'px)';
                                    thumbImg.addEventListener('mouseenter', function() {
                                        thumbImg.style.filter = 'none';
                                    });
                                    thumbImg.addEventListener('mouseleave', function() {
                                        thumbImg.style.filter = 'blur(' + config.thumbBlurAmount + 'px)';
                                    });
                                }

                                if (config.thumbHover) {
                                    thumbImg.addEventListener('mouseenter', function() {
                                        vrowPreview.style.display = null;
                                    });
                                    thumbImg.addEventListener('mouseleave', function() {
                                        vrowPreview.style.display = 'none';
                                    });
                                }
                                vcolThumb.appendChild(thumbImg);

                                thumbnailCreated = true;  // 썸네일 생성 완료
                            }
                            vrowPreview.style.display = 'none';
                            vrowPreview.style.pointerEvents = 'none';
                            vrowPreview.style.width = '30rem';
                            vrowPreview.style.height = 'auto';
                            vrowPreview.style.top = 'auto';
                            vrowPreview.style.left = (99) + parseFloat(config.thumbWidth) + 'px';
                            previewImg.src = previewImg.src.replace("&type=list", '');
                        }

                        function tryCreateThumbnail(retryCount) {
                            if (retryCount >= 100 || thumbnailCreated) return;  // 썸네일이 이미 생성되었으면 더 이상 시도하지 않음
                            setTimeout(function() {
                                if (retryCount === 0) createThumbnail();
                                tryCreateThumbnail(retryCount + 1);
                            }, 100);
                        }

                        tryCreateThumbnail(0);
                    }
                });
            });
        }

        // 썸네일 클릭 시 원본 이미지 불러오기
        if (config.origThumb) {
            document.querySelectorAll('a.title.preview-image').forEach(function(link) {
                link.addEventListener('click', function(event) {
                    event.preventDefault();  // 기본 동작 방지
                    var imageUrl = link.querySelector('img').getAttribute('src').replace(/&type=list/g, '');
                    window.location.href = imageUrl;
                });
            });
        }

        // 개념글 미리보기 이미지 마우스 오버시 보이게
        if (config.thumbHoverBest) {
            // 이미지 요소 선택
            var vrowPreviewImgs = document.querySelectorAll('.vrow.hybrid .title.preview-image .vrow-preview img');

            // 각 이미지 요소에 이벤트 추가
            vrowPreviewImgs.forEach(function(vrowPreviewImg) {
                // 이미지에 호버 이벤트 추가
                vrowPreviewImg.addEventListener('mouseenter', function() {
                    // 이미지의 부모 요소 찾기
                    var parentDiv = vrowPreviewImg.closest('.vrow.hybrid');

                    // 복제된 이미지 요소 생성
                    var duplicatevrowPreviewImg = document.createElement('img');
                    duplicatevrowPreviewImg.src = vrowPreviewImg.src.replace('&type=list', '');

                    // 복제된 이미지의 스타일 설정
                    duplicatevrowPreviewImg.style.position = 'absolute';
                    duplicatevrowPreviewImg.style.width = '30rem';
                    duplicatevrowPreviewImg.style.height = 'auto';
                    duplicatevrowPreviewImg.style.top = 'auto';
                    duplicatevrowPreviewImg.style.left = '7.5rem'; // 오른쪽으로 10rem 이동
                    duplicatevrowPreviewImg.style.zIndex = '1';
                    duplicatevrowPreviewImg.style.padding = '5px';
                    duplicatevrowPreviewImg.style.border = '1px solid';
                    duplicatevrowPreviewImg.style.borderRadius = '5px';
                    duplicatevrowPreviewImg.style.boxSizing = 'content-box';
                    duplicatevrowPreviewImg.style.backgroundColor = '#fff'; // 배경색
                    duplicatevrowPreviewImg.style.borderColor = '#bbb'; // 테두리 색상

                    // vrow hybrid 클래스에 align-items: center; 스타일 추가
                    parentDiv.classList.add('hybrid');
                    parentDiv.style.alignItems = 'center'; // 수직 가운데 정렬

                    // 복제된 이미지 요소를 기존 이미지 요소 다음에 추가
                    parentDiv.appendChild(duplicatevrowPreviewImg);

                    // 마우스를 이미지에서 떼었을 때 복제된 이미지 제거
                    vrowPreviewImg.addEventListener('mouseleave', function() {
                        duplicatevrowPreviewImg.remove();
                    });
                });
            });
        }

        if (config.controlButtons) {
            if ((config.closeButton || config.bookmarkButton || config.downButton)) {
                document.addEventListener('DOMContentLoaded', function () {
                    var articleMenu = document.querySelector('.article-menu.mt-2');
                    var originalScrapButton = articleMenu ? articleMenu.querySelector('.scrap-btn') : null;
                    var originalDownloadButton = articleMenu ? articleMenu.querySelector('#imageToZipBtn') : null;
                    var navControl = document.querySelector('.nav-control');

                    // 새로운 리스트 아이템 요소를 생성하는 함수
                    function createNewItem(iconClass, clickHandler, hoverHandler, leaveHandler) {
                        var newItem = document.createElement('li');
                        newItem.innerHTML = '<span class="' + iconClass + '"></span>';
                        newItem.addEventListener('click', clickHandler);
                        if (hoverHandler) {
                            newItem.addEventListener('mouseenter', hoverHandler);
                        }
                        if (leaveHandler) {
                            newItem.addEventListener('mouseleave', leaveHandler);
                        }
                        return newItem;
                    }

                    // 새로운 아이템을 내비게이션 컨트롤 리스트에 추가하거나 업데이트하는 함수
                    function appendOrUpdateItem(newItem) {
                        if (navControl) {
                            if (navControl.children.length > 0) {
                                navControl.insertBefore(newItem, navControl.firstElementChild);
                            } else {
                                navControl.appendChild(newItem);
                            }
                        } else {
                            console.error('내비게이션 컨트롤 리스트를 찾을 수 없습니다.');
                        }
                    }

                    // 다운로드 버튼 생성
                    if (config.controlButtons && config.downButton) {
                        if (articleMenu) {
                            var downloadButton = createNewItem(
                                'ion-android-download',
                                downloadButtonClickHandler,
                                downloadButtonHoverHandler,
                                downloadButtonLeaveHandler
                            );
                            appendOrUpdateItem(downloadButton);
                        }
                    }

                    // 다운로드 버튼 핸들러
                    function downloadButtonClickHandler() {
                        originalDownloadButton = articleMenu.querySelector('#imageToZipBtn');

                    if (originalDownloadButton) {
                        // 다운로드 버튼 클릭
                        originalDownloadButton.click();

                        var progressChecked = false; // 프로그레스 바가 50% 이상인지 체크하는 변수
                        var intervalId = setInterval(function () {
                            // 다운로드 진행 상태를 추적할 .download-progress 요소 찾기
                            var downloadProgress = originalDownloadButton.querySelector('.download-progress');

                            if (downloadProgress) {
                                // 프로그레스 바가 존재하면 진행 상태의 width 값 확인
                                var width = parseFloat(downloadProgress.style.width);

                                // 50% 이상이면 완료된 것으로 간주
                                if (width >= 50) {
                                    progressChecked = true;
                                }

                                // 프로그레스 바가 진행되면서 다운로드 버튼의 배경색을 조정
                                downloadButton.style.background = `
                                    linear-gradient(to top, green ${width}%, transparent ${width}%),
                                    #3d414d
                                `;

                                // 프로그레스 바가 100%에 도달했을 때
                                if (width >= 100) {
                                    clearInterval(intervalId); // 애니메이션 종료
                                    downloadButton.style.background = `
                                        linear-gradient(to top, green 100%, transparent 100%),
                                        #3d414d
                                    `;
                                }
                            } else {
                                // 프로그레스 바가 사라졌을 때 (프로그레스 바가 없을 때)
                                if (progressChecked) {
                                    // 프로그레스 바가 50% 이상이었다면 완료된 것으로 간주
                                    downloadButton.style.background = `
                                        linear-gradient(to top, green 100%, transparent 100%),
                                        #3d414d
                                    `;
                                } else {
                                    // 프로그레스 바가 50% 미만이었다면 취소로 간주
                                    downloadButton.style.background = `
                                        linear-gradient(to top, green 0%, transparent 0%),
                                        #3d414d
                                    `;
                                }
                                clearInterval(intervalId); // 애니메이션 종료
                            }
                        }, 10); // 10ms마다 확인
                    }

                    }
                    function downloadButtonHoverHandler() {
                        this.style.backgroundColor = 'green';
                    }
                    function downloadButtonLeaveHandler() {
                        this.style.backgroundColor = '';
                    }

                    // 북마크 버튼 생성
                    if (config.controlButtons && config.bookmarkButton) {
                        if (originalScrapButton) {
                            var bookmarkButton = createNewItem(
                                'ion-android-bookmark',
                                bookmarkButtonClickHandler,
                                bookmarkButtonHoverHandler,
                                bookmarkButtonLeaveHandler
                            );
                            appendOrUpdateItem(bookmarkButton);

                            // 북마크 버튼 색상을 업데이트하는 함수
                            function updateButtonColor() {
                                var buttonText = originalScrapButton.querySelector('.result').textContent.trim();
                                bookmarkButton.style.backgroundColor = (buttonText === "스크랩 됨") ? '#007bff' : '';
                            }

                            // 초기 호출 및 MutationObserver 설정
                            updateButtonColor();
                            var observer = new MutationObserver(updateButtonColor);
                            observer.observe(originalScrapButton.querySelector('.result'), { childList: true, subtree: true });
                        }
                    }

                    // 북마크 버튼 핸들러
                    function bookmarkButtonClickHandler() {
                        if (originalScrapButton) {
                            originalScrapButton.click();
                        } else {
                            console.error('원래의 스크랩 버튼을 찾을 수 없습니다.');
                        }
                    }
                    function bookmarkButtonHoverHandler() {
                        this.style.backgroundColor = '#007bff';
                    }
                    function bookmarkButtonLeaveHandler() {
                        var buttonText = originalScrapButton.querySelector('.result').textContent.trim();
                        this.style.backgroundColor = (buttonText === "스크랩 됨") ? '#007bff' : '';
                    }

                    // 닫기 버튼 생성 및 추가
                    if (config.controlButtons && config.closeButton) {
                        var closeButton = createNewItem(
                            'ion-close-round',
                            closeButtonClickHandler,
                            closeButtonHoverHandler,
                            closeButtonLeaveHandler
                        );
                        appendOrUpdateItem(closeButton);
                    }

                    // 닫기 버튼 핸들러
                    function closeButtonClickHandler() {
                        window.close();
                    }
                    function closeButtonHoverHandler() {
                        this.style.backgroundColor = 'red';
                    }
                    function closeButtonLeaveHandler() {
                        this.style.backgroundColor = '';
                    }
                });
            }
        }

        if (config.viewer) {
            let currentIndex = 0; // 현재 이미지 인덱스
            let images = []; // 게시글 내 이미지 배열
            let preloadedImages = [];  // 미리 로드된 이미지 배열
            let viewerElement = null; // 뷰어 엘리먼트
            let viewerImageContainer = null; // 뷰어 이미지 컨테이너
            let scrollbar = null; // 사용자 정의 스크롤바
            let counter = null; // 이미지 카운터
            let imageLayoutType = 'single'; // 기본: 한 장씩, 'single' or 'vertical'
            let scrollSpeed = 1; // 기본 스크롤 속도 (1x)

            function getImages() {
                images = Array.from(document.querySelectorAll(".fr-view.article-content img"))
                    .filter(img => !img.classList.contains("arca-emoticon"))
                    .map(img => img.src.startsWith("//") ? "https:" + img.src : img.src);
                console.log(`[이미지 뷰어] 이미지 목록: ${images}`);

                images.forEach((img, index) => {
                    const imgElement = document.querySelectorAll(".fr-view.article-content img")[index];
                    imgElement.style.cursor = "pointer";
                    imgElement.addEventListener("click", (event) => {
                        event.preventDefault();
                        currentIndex = index;
                        showViewer();
                    });
                });
            }

            function preloadImages() {
                // 이미지들을 미리 로드
                images.forEach(src => {
                    const img = new Image();
                    img.src = src; // 이미지를 로드
                    preloadedImages.push(img); // 로드된 이미지를 배열에 저장
                });
            }

            function createViewer() {
                const viewer = document.createElement("div");
                viewer.id = "imageViewer";
                viewer.style.cssText = `
                    position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
                    background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(2px); display: flex; flex-direction: column;
                    align-items: center; justify-content: center; z-index: 1036; overflow: hidden;`;

                const imgContainer = document.createElement("div");
                imgContainer.id = "imageContainer"; // 아이디 추가
                imgContainer.style.cssText = "display: flex; flex-direction: column; overflow: hidden; height: 100%; width: auto;";

                viewer.appendChild(imgContainer);
                document.body.appendChild(viewer);

                viewerElement = viewer;
                viewerImageContainer = imgContainer;

                createScrollbar();
                createCounter();
                createButtons();

                // 이미지들을 미리 로드 (뷰어가 생성될 때)
                preloadImages();

                viewer.addEventListener("click", (event) => {
                    if (event.target === viewerElement) {
                        closeViewer();
                    }
                });

                // 뷰어 전체에서 휠 이벤트 허용
                viewer.addEventListener("wheel", handleScroll);

                // 이미지 클릭 시 전체화면 토글
                viewerImageContainer.addEventListener("click", () => {
                    if (!document.fullscreenElement) {
                        // 전체화면으로 전환
                        viewerElement.requestFullscreen().catch((err) => {
                            console.error(`[이미지 뷰어] 전체 화면 전환 실패: ${err.message}`);
                        });
                    } else {
                        // 전체화면 해제
                        document.exitFullscreen().catch((err) => {
                            console.error(`[이미지 뷰어] 전체 화면 해제 실패: ${err.message}`);
                        });
                    }
                });
            }

            function handleScroll(event) {
                if (imageLayoutType === 'single') {
                    if (event.deltaY > 0 && currentIndex < images.length - 1) {
                        currentIndex++;
                    } else if (event.deltaY < 0 && currentIndex > 0) {
                        currentIndex--;
                    }
                    updateViewerImages();
                } else {
                    // 세로 스크롤 레이아웃에서는 deltaY를 변경하여 이동 거리 조정
                    viewerImageContainer.scrollTop += event.deltaY * scrollSpeed;
                    updateScrollbar();
                    updateCounter();
                    const scrollFraction = viewerImageContainer.scrollTop / viewerImageContainer.scrollHeight;
                    currentIndex = Math.round(scrollFraction * (images.length - 1));
                }
            }

            function createButtons() {
                const buttonContainer = document.createElement("div");
                buttonContainer.style.cssText = `
                    position: absolute; bottom: 25px; right: 140px; display: flex; flex-direction: row; gap: 15px;`;

                // 전체 화면 버튼
                const fullscreenButton = document.createElement("button");
                fullscreenButton.innerHTML = `
                    <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" fill="white">
                        <path d="M0 0h24v24H0z" fill="none"/>
                        <path d="M6 6h5V4H4v7h2V6zm12 0v5h2V4h-7v2h5zm-5 12h5v-5h2v7h-7v-2zm-7-5H4v7h7v-2H6v-5z"/>
                    </svg>`;
                fullscreenButton.style.cssText = `
                    background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.3);
                    border-radius: 50%; padding: 10px; cursor: pointer; display: flex; align-items: center;
                    justify-content: center; width: 50px; height: 50px; box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
                    user-select: none;  /* 텍스트 선택 방지 */
                `;
                fullscreenButton.addEventListener("click", () => {
                    if (!document.fullscreenElement) {
                        viewerElement.requestFullscreen().catch((err) => {
                            console.error(`[이미지 뷰어] 전체 화면 전환 실패: ${err.message}`);
                        });
                    } else {
                        document.exitFullscreen().catch((err) => {
                            console.error(`[이미지 뷰어] 전체 화면 해제 실패: ${err.message}`);
                        });
                    }
                });

                // 스크롤 속도 조정 버튼
                const speedButton = document.createElement("button");
                speedButton.innerHTML = `${scrollSpeed}x`;
                speedButton.style.cssText = `
                    background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.3);
                    border-radius: 50%; padding: 10px; cursor: pointer; display: flex; align-items: center;
                    justify-content: center; width: 50px; height: 50px; box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
                    user-select: none;  /* 텍스트 선택 방지 */
                `;
                speedButton.addEventListener("click", () => {
                    const speeds = [1, 1.5, 2, 3, 5, 10];
                    const currentIndex = speeds.indexOf(scrollSpeed);
                    scrollSpeed = speeds[(currentIndex + 1) % speeds.length]; // 다음 속도로 변경, 10x 이후 1x로 돌아감
                    speedButton.innerHTML = `${scrollSpeed}x`; // 버튼 텍스트 업데이트
                });

                // 레이아웃 토글 버튼
                const toggleLayoutButton = document.createElement("button");
                toggleLayoutButton.innerHTML = imageLayoutType === 'single' ? '1' : '2'; // 버튼 텍스트 동적으로 설정
                toggleLayoutButton.style.cssText = `
                    background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.3);
                    border-radius: 50%; padding: 10px; cursor: pointer; display: flex; align-items: center;
                    justify-content: center; width: 50px; height: 50px; box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
                    user-select: none;  /* 텍스트 선택 방지 */
                `;

                // IntersectionObserver를 활용하여 스크롤된 이미지의 인덱스 추적
                const observer = new IntersectionObserver((entries) => {
                    entries.forEach(entry => {
                        if (entry.isIntersecting) {
                            const index = images.indexOf(entry.target);
                            if (index !== -1) {
                                currentIndex = index;
                                console.log("현재 보이는 이미지 인덱스:", currentIndex);
                            }
                        }
                    });
                }, { threshold: 0.5 }); // 이미지가 50% 이상 보일 때 트리거

                images.forEach(image => {
                    if (image instanceof Element) { // image가 DOM 요소인지 확인
                        observer.observe(image);
                    }
                });

                toggleLayoutButton.addEventListener("click", () => {
                    if (imageLayoutType === 'single') {
                        imageLayoutType = 'vertical';
                        console.log(currentIndex);
                    } else {
                        imageLayoutType = 'single';
                    }

                    toggleLayoutButton.innerHTML = imageLayoutType === 'single' ? '1' : '2'; // 텍스트 업데이트
                    updateViewerImages();
                });

                buttonContainer.appendChild(toggleLayoutButton);
                buttonContainer.appendChild(speedButton);
                buttonContainer.appendChild(fullscreenButton);
                viewerElement.appendChild(buttonContainer);
            }

            function createScrollbar() {
                if (scrollbar) return;

                const scrollbarContainer = document.createElement("div");
                scrollbarContainer.style.cssText = `
                    position: absolute; right: 10px;
                    width: 50px; height: 80%; display: flex; justify-content: center; align-items: center;
                `;

                scrollbar = document.createElement("div");
                scrollbar.style.cssText = `
                    position: relative; width: 8px; height: 100%; background: rgba(255, 255, 255, 0.3);
                    border-radius: 4px; overflow: hidden; pointer-events: auto; cursor: pointer;
                `;

                const thumb = document.createElement("div");
                thumb.style.cssText = `
                    width: 100%; height: ${(1 / images.length) * 100}%;
                    background: rgba(255, 255, 255, 0.8); border-radius: 4px; position: absolute; top: 0;
                    transition: top 0.1s; cursor: grab;
                `;
                thumb.id = "scrollThumb";

                scrollbar.appendChild(thumb);
                scrollbarContainer.appendChild(scrollbar);
                viewerElement.appendChild(scrollbarContainer);

                // 스크롤바 클릭 이벤트
                scrollbar.addEventListener("click", (event) => {
                    const scrollbarRect = scrollbar.getBoundingClientRect();
                    const clickY = event.clientY - scrollbarRect.top;
                    const newThumbTop = Math.max(
                        0,
                        Math.min(clickY - thumb.offsetHeight / 2, scrollbarRect.height - thumb.offsetHeight)
                    );
                    thumb.style.top = `${newThumbTop}px`;

                    const scrollFraction = newThumbTop / (scrollbarRect.height - thumb.offsetHeight);
                    if (imageLayoutType === 'single') {
                        currentIndex = Math.round(scrollFraction * (images.length - 1));
                        updateViewerImages();
                    } else {
                        viewerImageContainer.scrollTop = scrollFraction * (viewerImageContainer.scrollHeight - viewerImageContainer.clientHeight);
                        updateCounter();
                    }
                });

                // Thumb 드래그 이벤트
                let isDragging = false;
                let startY = 0;
                let startTop = 0;

                thumb.addEventListener("mousedown", (event) => {
                    isDragging = true;
                    startY = event.clientY;
                    startTop = parseFloat(thumb.style.top);
                    thumb.style.cursor = "grabbing";
                    document.body.style.userSelect = "none"; // 텍스트 선택 방지
                });

                document.addEventListener("mousemove", (event) => {
                    if (!isDragging) return;

                    const deltaY = event.clientY - startY;
                    const scrollbarRect = scrollbar.getBoundingClientRect();
                    const newTop = Math.max(
                        0,
                        Math.min(startTop + deltaY, scrollbarRect.height - thumb.offsetHeight)
                    );
                    thumb.style.top = `${newTop}px`;

                    const scrollFraction = newTop / (scrollbarRect.height - thumb.offsetHeight);
                    if (imageLayoutType === 'single') {
                        currentIndex = Math.round(scrollFraction * (images.length - 1));
                        updateViewerImages();
                    } else {
                        viewerImageContainer.scrollTop = scrollFraction * (viewerImageContainer.scrollHeight - viewerImageContainer.clientHeight);
                        updateCounter();
                    }
                });

                document.addEventListener("mouseup", () => {
                    if (isDragging) {
                        isDragging = false;
                        thumb.style.cursor = "grab";
                        document.body.style.userSelect = ""; // 텍스트 선택 방지 해제
                    }
                });
            }

            function updateScrollbar() {
                if (!scrollbar) return;
                const thumb = scrollbar.querySelector("#scrollThumb");

                if (imageLayoutType === 'single') {
                    thumb.style.height = `${(1 / images.length) * 100}%`;

                    // 스크롤 thumb의 위치 계산
                    let thumbHeight = parseFloat(thumb.style.height);
                    let newTop = (currentIndex / (images.length - 1)) * (100 - thumbHeight);
                    thumb.style.top = `${newTop}%`;

                } else {
                    const containerHeight = viewerImageContainer.scrollHeight - viewerImageContainer.clientHeight;
                    const scrollTop = viewerImageContainer.scrollTop;
                    thumb.style.height = `${(viewerImageContainer.clientHeight / viewerImageContainer.scrollHeight) * 100}%`;

                    // 스크롤 thumb의 위치 계산
                    let thumbHeight = parseFloat(thumb.style.height);
                    let newTop = (scrollTop / containerHeight) * (100 - thumbHeight);
                    thumb.style.top = `${newTop}%`;
                }
            }

            function createCounter() {
                if (counter) return;

                counter = document.createElement("div");
                counter.style.cssText = `
                    position: absolute; bottom: 35px; right: 55px; color: white;
                    font-size: 14px; background: rgba(255, 255, 255, 0.1); padding: 5px 10px;
                    border-radius: 4px; user-select: none;`;
                viewerElement.appendChild(counter);
            }

            function updateCounter() {
                if (!counter) return;

                if (imageLayoutType === 'single') {
                    // 현재 이미지 인덱스 + 1 / 전체 이미지 개수
                    counter.textContent = `${currentIndex + 1} / ${images.length}`;
                } else {
                    // 세로 모드에서는 전체 높이에서 현재 스크롤 비율(%) 표시
                    const scrollFraction = viewerImageContainer.scrollTop / (viewerImageContainer.scrollHeight - viewerImageContainer.clientHeight);
                    const scrollPercent = Math.round(scrollFraction * 100);
                    counter.textContent = `${scrollPercent}%`;
                }
            }

            function showViewer() {
                if (!viewerElement) {
                    createViewer();
                }
                updateViewerImages();
                viewerElement.style.display = "flex";
                document.body.style.overflow = "hidden";
            }

            function closeViewer() {
                if (viewerElement) {
                    viewerElement.style.display = "none";
                }
                document.body.style.overflow = "";

                // 전체화면 상태인지 확인하고 종료
                if (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {
                    document.exitFullscreen();
                }
            }

            function updateViewerImages() {
                viewerImageContainer.innerHTML = "";

                if (imageLayoutType === 'single') {
                    const img = document.createElement("img");
                    img.src = images[currentIndex];
                    img.style.cssText = "height: 100%; width: auto; object-fit: contain;";
                    img.onload = function () {
                        updateScrollbar();
                        updateCounter();
                    };
                    viewerImageContainer.appendChild(img);
                } else {
                    viewerImageContainer.style.visibility = 'hidden';
                    const promises = images.map((src, index) => {
                        return new Promise((resolve) => {
                            const img = document.createElement("img");
                            img.src = src;
                            img.style.cssText = "width: 100%; object-fit: contain;";
                            img.onload = () => resolve(img);
                            viewerImageContainer.appendChild(img);
                        });
                    });

                    Promise.all(promises).then((imgs) => {
                        // 필요한 경우 스크롤 계산
                        const totalHeight = imgs.reduce((acc, img) => acc + img.height, 0);
                        const targetScrollTop = imgs
                            .slice(0, currentIndex)
                            .reduce((acc, img) => acc + img.height, 0);

                        viewerImageContainer.scrollTop = targetScrollTop;
                        viewerImageContainer.style.visibility = 'visible';

                        updateScrollbar();
                        updateCounter();
                    });
                }
            }

            getImages();  // 게시글에서 이미지 목록을 추출
        }
    }

    handleSettings();

    if (window.location.href.includes('scrap_list')) {
        if (config.scrapList){
            // arcaLiveScrapList();
            setTimeout(arcaLiveScrapList, 0);
        }
    }
    // arcaLive();
    setTimeout(arcaLive, 0);
})();