Humoruniv Simple Blind

간편한 블라인드 강화 기능

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Humoruniv Simple Blind
// @namespace    http://tampermonkey.net/
// @author       십갈
// @version      2.9
// @description  간편한 블라인드 강화 기능
// @match        https://web.humoruniv.com/*
// @exclude      https://web.humoruniv.com/board/humor/list.html?table=face*
// @exclude      https://web.humoruniv.com/board/humor/list.html?table=fashion*
// @exclude      https://web.humoruniv.com/cr/cr_list.html*
// @exclude      https://web.humoruniv.com/board/humor/report_ok.html?*
// @exclude      https://web.humoruniv.com/board/humor/cash_info.html?*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
// ==/UserScript==

(function () {
    'use strict';

    // Prevent the script from running in iframes
    if (window.self !== window.top) {
        return;
    }

    const memosKey = 'userMemos';
    const memoVisibilityKey = 'memoVisibility';
    let memos = GM_getValue(memosKey, []);
    let memoVisible = GM_getValue(memoVisibilityKey, true);
    let currentPage = 0;
    const memosPerPage = 5;

    // Create memo container
    const memoContainer = document.createElement('div');
    memoContainer.style.position = 'fixed';
    memoContainer.style.top = '10px';
    memoContainer.style.right = '10px';
    memoContainer.style.width = '200px';
    memoContainer.style.border = '1px solid #ccc';
    memoContainer.style.backgroundColor = '#f9f9f9';
    memoContainer.style.zIndex = '10000';
    memoContainer.style.padding = '10px';
    memoContainer.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.1)';
    memoContainer.style.maxHeight = '300px';
    memoContainer.style.overflow = 'auto';
    memoContainer.style.display = memoVisible ? 'block' : 'none';
    memoContainer.style.borderRadius = '8px';

    // Create memo input
    const memoInput = document.createElement('input');
    memoInput.type = 'text';
    memoInput.style.width = 'calc(100% - 22px)';
    memoInput.style.marginBottom = '10px';
    memoInput.style.border = '2px solid #007BFF'; // Blue border
    memoInput.style.backgroundColor = '#E9F7FF';  // Light blue background
    memoInput.style.padding = '5px';              // Add padding for better visibility
    memoInput.style.borderRadius = '4px';         // Rounded corners
    memoContainer.appendChild(memoInput);

    // Create add button
    const addButton = document.createElement('button');
    addButton.textContent = '추가';
    addButton.style.marginRight = '10px';
    addButton.style.padding = '5px 10px';
    addButton.style.backgroundColor = '#007BFF';
    addButton.style.color = 'white';
    addButton.style.border = 'none';
    addButton.style.borderRadius = '4px';
    addButton.style.cursor = 'pointer';
    addButton.style.fontSize = '14px';
    memoContainer.appendChild(addButton);

    // Create save button
    const saveButton = document.createElement('button');
    saveButton.textContent = '삭제 저장';
    saveButton.style.padding = '5px 10px';
    saveButton.style.backgroundColor = '#28a745';
    saveButton.style.color = 'white';
    saveButton.style.border = 'none';
    saveButton.style.borderRadius = '4px';
    saveButton.style.cursor = 'pointer';
    saveButton.style.fontSize = '14px';
    memoContainer.appendChild(saveButton);

    // Create hide button
    const hideButton = document.createElement('button');
    hideButton.textContent = '숨기기';
    hideButton.style.padding = '5px 10px';
    hideButton.style.backgroundColor = '#6c757d';
    hideButton.style.color = 'white';
    hideButton.style.border = 'none';
    hideButton.style.borderRadius = '4px';
    hideButton.style.cursor = 'pointer';
    hideButton.style.fontSize = '14px';
    memoContainer.appendChild(hideButton);

    // Create message display
    const messageDisplay = document.createElement('div');
    messageDisplay.style.color = 'red';
    messageDisplay.style.marginBottom = '10px';
    memoContainer.appendChild(messageDisplay);

    // Create memo list container
    const memoListContainer = document.createElement('div');
    memoContainer.appendChild(memoListContainer);

    // Create pagination controls
    const paginationControls = document.createElement('div');
    paginationControls.style.display = 'flex';
    paginationControls.style.justifyContent = 'space-between';
    paginationControls.style.alignItems = 'center'; // Align items vertically
    paginationControls.style.marginTop = '10px';

    const prevButton = document.createElement('button');
    prevButton.textContent = '<<<';
    prevButton.style.padding = '5px 10px';
    prevButton.style.backgroundColor = '#007BFF';
    prevButton.style.color = 'white';
    prevButton.style.border = 'none';
    prevButton.style.borderRadius = '4px';
    prevButton.style.cursor = 'pointer';
    prevButton.style.fontSize = '14px';
    prevButton.disabled = true;
    paginationControls.appendChild(prevButton);

    const pageIndicator = document.createElement('span');
    pageIndicator.style.fontSize = '14px';
    paginationControls.appendChild(pageIndicator);

    const nextButton = document.createElement('button');
    nextButton.textContent = '>>>';
    nextButton.style.padding = '5px 10px';
    nextButton.style.backgroundColor = '#007BFF';
    nextButton.style.color = 'white';
    nextButton.style.border = 'none';
    nextButton.style.borderRadius = '4px';
    nextButton.style.cursor = 'pointer';
    nextButton.style.fontSize = '14px';
    nextButton.disabled = true;
    paginationControls.appendChild(nextButton);

    memoContainer.appendChild(paginationControls);

    // Create show button
    const showButton = document.createElement('button');
    showButton.textContent = '블라인드';
    showButton.style.position = 'fixed';
    showButton.style.top = '10px';
    showButton.style.right = '10px';
    showButton.style.padding = '10px 20px';
    showButton.style.backgroundColor = '#28a745';
    showButton.style.color = 'white';
    showButton.style.border = 'none';
    showButton.style.borderRadius = '4px';
    showButton.style.cursor = 'pointer';
    showButton.style.fontSize = '14px';
    showButton.style.display = memoVisible ? 'none' : 'block';

    // Create memo count display
    const memoCountDisplay = document.createElement('span');
    memoCountDisplay.style.position = 'fixed';
    memoCountDisplay.style.top = '40px';
    memoCountDisplay.style.right = '10px';
    memoCountDisplay.style.padding = '2px 5px';
    memoCountDisplay.style.backgroundColor = '#dc3545';
    memoCountDisplay.style.color = 'white';
    memoCountDisplay.style.borderRadius = '4px';
    memoCountDisplay.style.fontSize = '12px';
    memoCountDisplay.style.display = memoVisible ? 'none' : 'block';
    memoCountDisplay.textContent = memos.length;

    document.body.appendChild(showButton);
    document.body.appendChild(memoContainer);
    document.body.appendChild(memoCountDisplay);

    const commentBlindContainer = document.createElement('div');
    commentBlindContainer.style.position = 'fixed';
    commentBlindContainer.style.bottom = '10px';
    commentBlindContainer.style.right = '10px';
    commentBlindContainer.style.width = '200px';
    commentBlindContainer.style.border = '1px solid #ccc';
    commentBlindContainer.style.backgroundColor = '#f9f9f9';
    commentBlindContainer.style.zIndex = '10000';
    commentBlindContainer.style.padding = '10px';
    commentBlindContainer.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.1)';
    commentBlindContainer.style.borderRadius = '8px';
    commentBlindContainer.innerHTML = `
            <button id="showAllButton" style="padding: 5px 10px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">모두 보기</button>
            <button id="hideAllButton" style="padding: 5px 10px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px;">모두 가리기</button>
        `;
    document.body.appendChild(commentBlindContainer);

    document.getElementById('showAllButton').addEventListener('click', showAll);
    document.getElementById('hideAllButton').addEventListener('click', hideAll);

    function updateMemoList() {
        memoListContainer.innerHTML = '';
        const start = currentPage * memosPerPage;
        const end = Math.min(start + memosPerPage, memos.length);
        const currentMemos = memos.slice(start, end);

        currentMemos.forEach((memo, index) => {
            const memoItem = document.createElement('div');
            memoItem.style.display = 'flex';
            memoItem.style.alignItems = 'center';
            memoItem.style.marginBottom = '5px';

            const memoCheckbox = document.createElement('input');
            memoCheckbox.type = 'checkbox';
            memoCheckbox.style.marginRight = '10px';
            memoCheckbox.style.zoom = 1.5;
            memoCheckbox.checked = true;
            memoCheckbox.addEventListener('change', () => {
                if (memoCheckbox.checked) {
                    memos[start + index] = memo;
                } else {
                    memos[start + index] = null;
                }
            });

            const memoText = document.createElement('span');
            memoText.textContent = memo;

            memoItem.appendChild(memoCheckbox);
            memoItem.appendChild(memoText);
            memoListContainer.appendChild(memoItem);
        });
    }

    function updatePaginationControls() {
        prevButton.disabled = currentPage === 0;
        nextButton.disabled = (currentPage + 1) * memosPerPage >= memos.length;
        const totalPages = Math.max(1, Math.ceil(memos.length / memosPerPage));
        pageIndicator.textContent = `${currentPage + 1} / ${totalPages}`;
    }

    function addMemo() {
        const newMemo = memoInput.value.trim(); // Use value instead of textContent
        if (newMemo === '') return;
        if (memos.includes(newMemo)) {
            if (checkboxClicked === true) {
                messageDisplay.textContent = '';
            } else {
                messageDisplay.textContent = '이미 등록된 사용자입니다.';
            }
        } else {
            messageDisplay.textContent = '';
            memos.unshift(newMemo);
            GM_setValue(memosKey, memos);
            updateMemoList();
            updatePaginationControls();
            applyFilter(); // Reapply filter to ensure new memos have the checkboxes
        }
        memoInput.value = '';
    }

    addButton.addEventListener('click', addMemo); // Pass function reference instead of calling it
    memoInput.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') {
            addMemo();
        }
    });

    prevButton.addEventListener('click', () => {
        if (currentPage > 0) {
            currentPage--;
            updateMemoList();
            updatePaginationControls();
        }
    });

    nextButton.addEventListener('click', () => {
        if ((currentPage + 1) * memosPerPage < memos.length) {
            currentPage++;
            updateMemoList();
            updatePaginationControls();
        }
    });

    saveButton.addEventListener('click', () => {
        memos = memos.filter(memo => memo !== null);
        GM_setValue(memosKey, memos);
        updateMemoList();
        updatePaginationControls();
        applyFilter();
        memoCountDisplay.textContent = memos.length;
    });

    hideButton.addEventListener('click', () => {
        memoContainer.style.display = 'none';
        showButton.style.display = 'block';
        memoCountDisplay.style.display = 'block';
        GM_setValue(memoVisibilityKey, false);
    });

    showButton.addEventListener('click', () => {
        memoContainer.style.display = 'block';
        showButton.style.display = 'none';
        memoCountDisplay.style.display = 'none';
        GM_setValue(memoVisibilityKey, true);
    });

    var checkboxClicked
    function applyFilter() {
        if (window.location.href.includes('https://web.humoruniv.com/user/blind_list.html')
            || ((window.location.href.includes('st=name') || window.location.href.includes('st=subject')) && !window.location.href.includes('read.html'))) {
            return;
        }
        checkboxClicked = false;
        document.querySelectorAll('span.hu_nick_txt').forEach(span => {
            let spanText = span.textContent.trim();
            if (spanText.length === 0) {
                spanText = [...span.querySelectorAll('span')].find(child => child.textContent.length > 0).textContent.trim();
            }
            if (!span.closest('#profile_table > tbody > tr') && !span.closest('span.nick > span > span') && !span.closest('#login_box_mem > dl > dd.a > span > span > span')) {
                var toggleCheckboxHTML;
                let closestTr = span.closest('tr');
                if (closestTr) {
                    if (window.location.href.includes('list.html')) {
                        closestTr = closestTr.closest('tbody').closest('tr');
                    }
                    if (memos.includes(spanText)) {
                        const listBestBox = closestTr.querySelector('td > div#list_best_box');
                        const tdsToHide = listBestBox ? Array.from(closestTr.querySelectorAll('td')).slice(1, 3) : Array.from(closestTr.querySelectorAll('td')).slice(0, 3);
                        tdsToHide.forEach(td => {
                            if (!td.querySelector('.blind-overlay')) {
                                const overlay = document.createElement('div');
                                overlay.className = 'blind-overlay';
                                overlay.style.position = 'absolute';
                                overlay.style.top = '0';
                                overlay.style.left = '0';
                                overlay.style.width = '100%';
                                overlay.style.height = '100%';
                                overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.97)'; // Set opacity to 97%
                                overlay.style.pointerEvents = 'none';
                                td.style.position = 'relative';
                                td.appendChild(overlay);
                            }
                        });
                        // Check if checkboxes already exist
                        if (!closestTr.querySelector('.toggle-hide-checkbox')) {
                            toggleCheckboxHTML = `
                             <tz><br><input type="checkbox" class="toggle-hide-checkbox"><label>보기</label></tz>
                            `
                            if (!closestTr.querySelector('.toggle-blind-checkbox')) {
                                toggleCheckboxHTML = `
                                <tz><br><input type="checkbox" class="toggle-blind-checkbox" checked><label>블라</label></tz>
                            ` + toggleCheckboxHTML;
                            } else {
                                closestTr.querySelector('.toggle-blind-checkbox').checked = true;
                            }
                            closestTr.children[3].children[1].insertAdjacentHTML('beforeend', toggleCheckboxHTML);

                            const toggleHideCheckbox = closestTr.querySelector('.toggle-hide-checkbox');
                            if (toggleHideCheckbox && toggleHideCheckbox.getAttribute('listener') !== 'true') {
                                toggleHideCheckbox.addEventListener('change', (e) => {
                                    const elementChanged = e.target;
                                    elementChanged.setAttribute('listener', 'true')
                                    const isChecked = toggleHideCheckbox.checked;
                                    tdsToHide.forEach(td => {
                                        const overlay = td.querySelector('.blind-overlay');
                                        if (overlay) {
                                            overlay.style.display = isChecked ? 'none' : 'block';
                                        }
                                    });
                                });
                            }
                        }
                    } else if (closestTr.querySelector('.toggle-hide-checkbox')) {
                        closestTr.querySelector('tz:nth-child(2)').remove()
                        const tdsToHide = Array.from(closestTr.querySelectorAll('td')).slice(0, 3);
                        tdsToHide.forEach(td => {
                            if (td.querySelector('.blind-overlay')) {
                                td.querySelector('.blind-overlay').remove()
                            }
                        });
                        closestTr.querySelector('.toggle-blind-checkbox').checked = false;
                    } else if (!closestTr.querySelector('.toggle-blind-checkbox')) {
                        toggleCheckboxHTML = `
                                <tz><br><input type="checkbox" class="toggle-blind-checkbox"><label>블라</label></tz>
                            `;
                        closestTr.children[3].children[1].insertAdjacentHTML('beforeend', toggleCheckboxHTML);
                        closestTr.querySelector('.toggle-blind-checkbox').checked = false;

                    } else {
                        closestTr.querySelector('.toggle-blind-checkbox').checked = false;
                    }
                    const toggleBlindCheckbox = closestTr.querySelector('.toggle-blind-checkbox');
                    if (toggleBlindCheckbox && toggleBlindCheckbox.getAttribute('listener') !== 'true') {
                        toggleBlindCheckbox.addEventListener('change', (e) => {
                            checkboxClicked = true;
                            const elementChanged = e.target;
                            elementChanged.setAttribute('listener', 'true')
                            const isChecked = toggleBlindCheckbox.checked;
                            if (isChecked) {
                                const span = toggleBlindCheckbox.closest('tr').querySelector('span.hu_nick_txt');
                                let spanText = span.textContent.trim();
                                if (spanText.length === 0) {
                                    spanText = [...span.querySelectorAll('span')].find(child => child.textContent.length > 0).textContent.trim();
                                }
                                console.log(spanText);
                                memoInput.value = spanText;
                                addMemo();
                            } else {
                                memos = memos.filter(memo => memo !== spanText);
                                GM_setValue(memosKey, memos);
                                updateMemoList();
                                updatePaginationControls();
                                applyFilter();
                            }
                        });

                    }
                }
            }
        });
    }

    function showAll() {
        document.querySelectorAll('.blind-overlay').forEach(overlay => {
            overlay.style.display = 'none';
        });
        document.querySelectorAll('.toggle-hide-checkbox').forEach(checkbox => {
            checkbox.checked = true;
        });
    }

    function hideAll() {
        document.querySelectorAll('.toggle-hide-checkbox').forEach(checkbox => {
            checkbox.checked = false;
            checkbox.dispatchEvent(new Event('change'));
        });
    }

    // Initialize the memo list, pagination controls, and apply filter
    updateMemoList();
    updatePaginationControls();
    applyFilter();

    // Listen for changes to memos and re-apply filter if necessary
    GM_addValueChangeListener(memosKey, () => {
        memos = GM_getValue(memosKey, []);
        updateMemoList();
        applyFilter();
        memoCountDisplay.textContent = memos.length;
    });
})();