您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
자동 새로고침 (옵션 및 블라인드 탭 추가)
// ==UserScript== // @name Humoruniv Auto Refresh & Simple blind // @namespace http://tampermonkey.net/ // @version 2.46 // @description 자동 새로고침 (옵션 및 블라인드 탭 추가) // @author 십갈 // @match https://web.humoruniv.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=humoruniv.com // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // ==/UserScript== (function () { 'use strict'; if (window.self !== window.top) { return; } const settings = { autoRefresh: GM_getValue('autoRefresh', true), blinkEffect: GM_getValue('blinkEffect', true), blindList: GM_getValue('blindList', {}).reduce((acc, key) => { acc[key] = 0; return acc; }, {}) }; function saveSettings() { GM_setValue('autoRefresh', settings.autoRefresh); GM_setValue('blinkEffect', settings.blinkEffect); GM_setValue('blindList', Object.keys(settings.blindList)); } function resetBlindValues() { Object.keys(settings.blindList).forEach(key => { settings.blindList[key] = 0; }); } function updateBlindListDisplay() { const blindList = document.getElementById('blindList'); const currentPage = parseInt(document.getElementById('currentPage').value, 10); const itemsPerPage = 10; const keys = Object.keys(settings.blindList); const totalPages = Math.max(Math.ceil(keys.length / itemsPerPage), 1); const startIndex = (currentPage - 1) * itemsPerPage; const endIndex = startIndex + itemsPerPage; const pageItems = keys.slice(startIndex, endIndex).map(key => `<li class="mui-list-item ${settings.blindList[key] ? 'red' : ''}" data-key="${key}">${key}</li>`); blindList.innerHTML = pageItems.join(''); document.getElementById('totalPages').textContent = `/ ${totalPages}`; seeViewAll(); } function seeButtonVisible(tr) { const span = tr.querySelector('span.hu_nick_txt'); if (!span) return; let textContent = span.textContent.trim(); if (textContent.length === 0) { textContent = [...span.querySelectorAll('span')].find(child => child.textContent.length > 0).textContent.trim(); } const viewButton = tr.querySelector('button.view-button'); if (viewButton) { if (settings.blindList.hasOwnProperty(textContent)) { viewButton.classList.remove('hidden-button'); viewButton.classList.add('revealed-button'); targetOverlay(tr, 'block') } else { viewButton.classList.remove('revealed-button'); viewButton.classList.add('hidden-button'); targetOverlay(tr, 'none') } } } const settingsPanelHTML = ` <div id="settingsPanel" class="mui-panel"> <div class="mui-tabs"> <button id="settingsTab" class="mui-tab mui-active">옵션</button> <button id="blindTab" class="mui-tab">블라인드</button> </div> <div id="optionsContent" class="mui-tab-content"> <label class="mui-checkbox"> <input type="checkbox" id="autoRefresh" ${settings.autoRefresh ? 'checked' : ''}> 자동 새로고침 </label><br> <label class="mui-checkbox"> <input type="checkbox" id="blinkEffect" ${settings.blinkEffect ? 'checked' : ''}> 반짝임 효과 </label><br> <button id="saveSettings" class="mui-btn mui-btn--primary mui-btn--raised">저장</button> </div> <div id="blindContent" class="mui-tab-content" style="display: none;"> <div class="mui-pagination"> <button id="prevPage" class="mui-btn mui-btn--primary mui-pagination-button"><font size=1>이전</font></button> <input type="text" id="currentPage" value="1" class="mui-input mui-pagination-input"> <span id="totalPages">/ 1</span> <button id="nextPage" class="mui-btn mui-btn--primary mui-pagination-button"><font size=1>다음</font></button> <button id="deleteBlind" class="mui-btn mui-btn-primary"><font size=2>삭제</font></button> </div> <ul id="blindList" class="mui-list"></ul> </div> </div> <div id="settingsButton" class="mui-fab" text-align: center><font size=6>⋮</font></div> `; GM_addStyle(` .mui-panel { display: none; position: fixed; top: 50px; right: 50px; width: 230px; background: #ffffff; border: 1px solid #e0e0e0; padding: 10px; z-index: 9999; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 8px; } .mui-tabs { display: flex; border-bottom: 1px solid #e0e0e0; } .mui-tab { flex: 1; padding: 10px; text-align: center; cursor: pointer; background: #f5f5f5; border: none; outline: none; border-bottom: 2px solid transparent; color: #333; border-radius: 8px 8px 0 0; } .mui-active { background: white; border-bottom: 2px solid #007bff; } .mui-tab-content { padding: 10px 0; color: #333; } .mui-checkbox { display: block; margin: 10px 0; } .mui-list { list-style-type: none; padding: 0; margin: 0; } .mui-list-item { border: 1px solid #e0e0e0; padding: 10px; margin: 5px 0; border-radius: 8px; cursor: pointer; background: #fafafa; color: #333; } .mui-pagination { display: flex; align-items: center; justify-content: center; margin-top: 10px; } .mui-fab { position: fixed; top: 10px; right: 10px; width: 30px; height: 30px; background: #007bff; color: white; border: none; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .mui-btn { margin: 0 5px; background: #007bff; color: white; border: none; border-radius: 4px; padding: 5px 10px; cursor: pointer; transition: background 0.3s; } .mui-btn:hover { background: #0056b3; } .mui-input { margin: 0 5px; padding: 5px; border: 1px solid #e0e0e0; border-radius: 4px; outline: none; width: 40px; text-align: center; color: #333; background: #fafafa; } .hidden-button { visibility: hidden; } .revealed-button { visibility: visible; } .red { background-color: #ff1744; color: white; } .mui-button { margin: 2px; padding: 2px 5px; border: none; border-radius: 4px; background-color: #007bff; color: white; cursor: pointer; outline: none; display: inline-block; font-size: 10px; } .mui-button + .mui-button { margin-left: 5px; } .mui-pagination-button { border-radius: 4px; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; } `); document.body.insertAdjacentHTML('beforeend', settingsPanelHTML); document.getElementById('settingsButton').addEventListener('click', () => { const panel = document.getElementById('settingsPanel'); panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; if (panel.style.display === 'none') { resetBlindValues(); saveSettings(); } }); document.getElementById('settingsTab').addEventListener('click', () => { document.getElementById('optionsContent').style.display = 'block'; document.getElementById('blindContent').style.display = 'none'; document.getElementById('settingsTab').classList.add('mui-active'); document.getElementById('blindTab').classList.remove('mui-active'); }); document.getElementById('blindTab').addEventListener('click', () => { document.getElementById('optionsContent').style.display = 'none'; document.getElementById('blindContent').style.display = 'block'; document.getElementById('settingsTab').classList.remove('mui-active'); document.getElementById('blindTab').classList.add('mui-active'); updateBlindListDisplay(); }); document.getElementById('saveSettings').addEventListener('click', () => { settings.autoRefresh = document.getElementById('autoRefresh').checked; settings.blinkEffect = document.getElementById('blinkEffect').checked; saveSettings(); alert('설정이 저장되었습니다.'); }); document.getElementById('blindList').addEventListener('click', (event) => { const target = event.target; if (target.tagName === 'LI' && target.dataset.key) { const key = target.dataset.key; settings.blindList[key] = settings.blindList[key] === 0 ? 1 : 0; target.classList.toggle('red', settings.blindList[key]); saveSettings(); } }); document.getElementById('deleteBlind').addEventListener('click', () => { Object.keys(settings.blindList).forEach(key => { if (settings.blindList[key]) { delete settings.blindList[key]; } }); updateBlindListDisplay(); saveSettings(); }); document.getElementById('prevPage').addEventListener('click', () => { let currentPage = parseInt(document.getElementById('currentPage').value, 10); if (currentPage > 1) { document.getElementById('currentPage').value = currentPage - 1; updateBlindListDisplay(); } }); document.getElementById('nextPage').addEventListener('click', () => { let currentPage = parseInt(document.getElementById('currentPage').value, 10); let totalPages = Math.max(Math.ceil(Object.keys(settings.blindList).length / 10), 1); if (currentPage < totalPages) { document.getElementById('currentPage').value = currentPage + 1; updateBlindListDisplay(); } }); const remotePageUrl = location.href; const maxChildNodes = 20; let shouldSync = settings.autoRefresh; let initialAdded = false; const style = document.createElement('style'); style.textContent = ` @keyframes blink-light { 0%, 100% { background-color: transparent; } 50% { background-color: #007bff; } } .blink-light { animation: blink-light 0.5s ease-in-out 10; } @keyframes blink-dark { 0%, 100% { background-color: transparent; } 50% { background-color: #007bff; } } .blink-dark { animation: blink-dark 0.5s ease-in-out 10; } .hidden-button { visibility: hidden; } .revealed-button { visibility: visible; } `; document.head.appendChild(style); function getBlinkClass() { const darkThemeMatch = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; return darkThemeMatch ? 'blink-dark' : 'blink-light'; } function syncElements(remoteDoc) { if (shouldSync) { syncSection('#post_list > tbody', remoteDoc, syncNodesWithFIFO); refreshAdditionalElements(remoteDoc); } } function syncSection(selector, remoteDoc, syncFunction) { const currentElement = document.querySelector(selector); const remoteElement = remoteDoc.querySelector(selector); if (currentElement && remoteElement) { syncFunction(currentElement, remoteElement); } } function getCurrentFirstChildIdNumber(currentElement) { const firstChild = currentElement.querySelector('tr'); if (firstChild && firstChild.id) { const match = firstChild.id.match(/li_chk_pdswait-(\d+)/); return match ? parseInt(match[1], 10) : 0; } return 0; } function syncNodesWithFIFO(currentNode, remoteNode) { try { const currentFirstIdNumber = getCurrentFirstChildIdNumber(currentNode); const remoteChildren = Array.from(remoteNode.querySelectorAll(':scope > tr')); const newChildren = remoteChildren.filter(child => { const match = child.id.match(/li_chk_pdswait-(\d+)/); const remoteIdNumber = match ? parseInt(match[1], 10) : 0; return remoteIdNumber > currentFirstIdNumber; }); newChildren.reverse().forEach(newChild => { const clonedChild = newChild.cloneNode(true); addButtons(clonedChild); addOverlay(clonedChild, 'none'); let targetSpan = clonedChild.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span span') || clonedChild.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span'); let userName = targetSpan ? targetSpan.textContent.trim() : null; if (userName) { addOverlay(clonedChild, 'none'); if (settings.blindList.hasOwnProperty(userName)) { targetOverlay(clonedChild, 'block') seeButtonVisible(clonedChild) } } currentNode.insertBefore(clonedChild, currentNode.firstChild); const nextSibling = newChild.nextElementSibling; if (nextSibling && nextSibling.tagName.toLowerCase() === 'script') { const clonedScript = nextSibling.cloneNode(true); currentNode.insertBefore(clonedScript, currentNode.firstChild.nextSibling); } }); const trChildren = Array.from(currentNode.querySelectorAll(':scope > tr')); while (trChildren.length > maxChildNodes) { const lastChild = trChildren.pop(); if (lastChild && currentNode.contains(lastChild)) { const nextSibling = lastChild.nextElementSibling; if (nextSibling && nextSibling.tagName.toLowerCase() === 'script' && currentNode.contains(nextSibling)) { currentNode.removeChild(nextSibling); } if (currentNode.contains(lastChild)) { currentNode.removeChild(lastChild); } } } refreshRemainingNodes(currentNode, remoteNode); } catch (error) { console.error('Error in syncNodesWithFIFO:', error); } } function refreshRemainingNodes(currentNode, remoteNode) { try { const currentChildren = Array.from(currentNode.querySelectorAll(':scope > tr')); const remoteChildren = Array.from(remoteNode.querySelectorAll(':scope > tr')); currentChildren.forEach((currentChild) => { const remoteChild = remoteChildren.find(remote => remote.id === currentChild.id); const textContent = currentChild.querySelector('td.li_icn').textContent.trim(); const isInBlindList = settings.blindList.hasOwnProperty(textContent); if (remoteChild) { const currentChildChildren = Array.from(currentChild.children); const remoteChildChildren = Array.from(remoteChild.children); [1, 4, 5, 6].forEach(childIndex => { if (childIndex === 1) { const currentChildNode = currentChildChildren[childIndex]; const remoteChildNode = remoteChildChildren[childIndex]; if (currentChildNode && remoteChildNode && currentChildNode.textContent !== remoteChildNode.textContent) { currentChildNode.querySelector('td.li_sbj > a').innerHTML = remoteChildNode.querySelector('td.li_sbj > a').innerHTML; if (settings.blinkEffect && !isInBlindList) { blinkElement(currentChildNode); } } } else { const currentChildNode = currentChildChildren[childIndex]; const remoteChildNode = remoteChildChildren[childIndex]; if (currentChildNode && remoteChildNode && currentChildNode.textContent !== remoteChildNode.textContent) { currentChildNode.innerHTML = remoteChildNode.innerHTML; if (settings.blinkEffect && !isInBlindList) { blinkElement(currentChildNode); } } } }); } else { const nextSibling = currentChild.nextElementSibling; if (nextSibling && nextSibling.tagName.toLowerCase() === 'script' && currentNode.contains(nextSibling)) { currentNode.removeChild(nextSibling); } currentChild.remove(); } }); } catch (error) { console.error('Error in refreshRemainingNodes:', error); } } function blinkElement(element) { const blinkClass = getBlinkClass(); element.classList.add(blinkClass); setTimeout(() => { element.classList.remove(blinkClass); }, 2000); } function addButtons(trElement) { if (trElement) { const fourthChild = trElement.children[3]; if (!fourthChild) { return; } const blindButton = document.createElement('button'); const viewButton = document.createElement('button'); blindButton.textContent = '블라'; viewButton.textContent = '보기'; viewButton.classList.add('hidden-button', 'view-button'); blindButton.classList.add('mui-button'); viewButton.classList.add('mui-button'); console.log('블라,보기 버튼 설치 완료') blindButton.addEventListener('click', () => { var targetSpan let commentList = trElement.childElementCount === 6 if (commentList) { targetSpan = trElement.querySelector('td:nth-child(2) > span > span') || trElement.querySelector('td:nth-child(2) > div:nth-child(2) > span > span'); } else { targetSpan = trElement.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span span') || trElement.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span'); } let userName = targetSpan.textContent.trim(); console.log('당신의 이름은 : ' + userName) if (userName && settings.blindList.hasOwnProperty(userName)) { delete settings.blindList[userName]; console.log('blind delete'); if (commentList) { document.querySelectorAll('#cmt_wrap_box > table > tbody > tr').forEach((tr) => { let targetSpanN = tr.querySelector('div span span span') || tr.querySelector('div span span'); let userNameN = targetSpanN ? targetSpanN.textContent.trim() : null; if (userNameN === userName) { targetOverlay(tr, 'none'); } }); } else { document.querySelectorAll('#post_list > tbody > tr').forEach((tr) => { let targetSpanN = tr.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span span') || tr.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span'); let userNameN = targetSpanN ? targetSpanN.textContent.trim() : null; if (userNameN === userName) { targetOverlay(tr, 'none'); } }) } } else if (userName && !settings.blindList.hasOwnProperty(userName)) { settings.blindList[userName] = 0; if (commentList) { document.querySelectorAll('#cmt_wrap_box > table > tbody > tr').forEach((tr) => { let targetSpanN = tr.querySelector('div span span span') || tr.querySelector('div span span'); let userNameN = targetSpanN ? targetSpanN.textContent.trim() : null; if (userNameN === userName) { targetOverlay(tr, 'block'); } }); } else { document.querySelectorAll('#post_list > tbody > tr').forEach((tr) => { let targetSpanN = tr.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span span') || tr.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span'); let userNameN = targetSpanN ? targetSpanN.textContent.trim() : null; if (userNameN === userName) { targetOverlay(tr, 'block'); } }); } } updateBlindListDisplay(); saveSettings(); }); console.log('블라 이벤트 리스너 설치 완료') viewButton.addEventListener('click', () => { const overlays = trElement.querySelectorAll('.overlay'); if (trElement.children.length === 6 && trElement.querySelector('#list_best_box')) { var overlayArray = [...trElement.querySelectorAll('.overlay')].slice(1); } else { var overlayArray = [...trElement.querySelectorAll('.overlay')]; } overlayArray.forEach(overlay => { if (overlay.style.display === 'none') { overlay.style.display = 'block'; } else { overlay.style.display = 'none'; } }); }); fourthChild.appendChild(blindButton); fourthChild.appendChild(viewButton); if (trElement.childElementCount === 6) { viewButton.insertAdjacentHTML('beforebegin', '<br>') } console.log('보기 이벤트 리스너 설치 완료') } } function addOverlay(trElement, status) { [0, 1, 2].forEach(i => { if (trElement.children[i] && !trElement.children[i].querySelector('.overlay')) { const overlay = document.createElement('div'); overlay.className = 'overlay'; overlay.style.position = 'absolute'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.97)'; overlay.style.zIndex = '1000'; trElement.children[i].style.position = 'relative'; trElement.children[i].appendChild(overlay); overlay.style.display = status; } }); } function toggleOverlay(trElement) { const overlays = trElement.querySelectorAll('.overlay'); overlays.forEach(overlay => { if (overlay.style.display === 'none') { overlay.style.display = 'block'; } else { overlay.style.display = 'none'; } }); } function targetOverlay(trElement, status) { var overlays if (trElement.children.length === 6 && trElement.querySelector('#list_best_box')) { overlays = [...trElement.querySelectorAll('.overlay')].slice(1); } else { overlays = [...trElement.querySelectorAll('.overlay')]; } overlays.forEach(overlay => { overlay.style.display = status; }); } function refreshAdditionalElements(remoteDoc) { try { const selectors = [ '#login_box_mem > dl > dd:nth-child(3) > a', '#login_box_mem > dl > dd:nth-child(2) > a', '#bbs_best' ]; selectors.forEach(selector => { const currentElement = document.querySelector(selector); const remoteElement = remoteDoc.querySelector(selector); if (currentElement && remoteElement) { currentElement.innerHTML = remoteElement.innerHTML; } }); const iframe = document.querySelector('#list_right_idx > div:nth-child(2) > iframe'); if (iframe) { iframe.addEventListener('load', () => { const iframeDocument = iframe.contentDocument || iframe.contentWindow.document; const iframeRemoteDoc = remoteDoc.querySelector('#list_right_idx > div:nth-child(2) > iframe').contentDocument || remoteDoc.querySelector('#list_right_idx > div:nth-child(2) > iframe').contentWindow.document; const iframeCurrentElement = iframeDocument.querySelector('#wrap_right_list_new2 > div.df.wrap_sub_best_new'); const iframeRemoteElement = iframeRemoteDoc.querySelector('#wrap_right_list_new2 > div.df.wrap_sub_best_new'); if (iframeCurrentElement && iframeRemoteElement) { iframeCurrentElement.innerHTML = iframeRemoteElement.innerHTML; } }); } } catch (error) { console.error('Error in refreshAdditionalElements:', error); } } function fetchAndSyncRemotePage() { if (settings.autoRefresh && restricted()) { fetch(remotePageUrl, { headers: { 'Content-Type': 'text/html; charset=euc-kr' } }) .then(response => response.arrayBuffer()) .then(buffer => { const decoder = new TextDecoder('euc-kr'); const html = decoder.decode(buffer); const parser = new DOMParser(); const remoteDoc = parser.parseFromString(html, 'text/html'); syncElements(remoteDoc); }) .catch(error => console.error('Error fetching remote page:', error)); } } function observeIframe() { const observer = new MutationObserver(() => { const balloonIframe = document.querySelector('iframe[name="balloon"]'); shouldSync = settings.autoRefresh && !balloonIframe; }); observer.observe(document.body, { childList: true, subtree: true }); } function restricted() { const url = location.href; if (url.includes('&st=')){ return false; } else { return true; } } function seeViewAll() { document.querySelectorAll('#post_list > tbody > tr').forEach(seeButtonVisible); console.log('대자에 보기버튼 추가'); document.querySelectorAll('#cmt_wrap_box > table > tbody > tr').forEach(seeButtonVisible); console.log('답글에 보기버튼 추가'); } if (!initialAdded) { seeViewAll(); initialAdded = true; console.log('초기화 성공'); } // 댓글 창인 경우 document.querySelectorAll('#cmt_wrap_box > table > tbody > tr').forEach((trElement) => { addButtons(trElement) let targetSpan = trElement.querySelector('div span span span') || trElement.querySelector('div span span'); let userName = targetSpan ? targetSpan.textContent.trim() : null; if (userName) { addOverlay(trElement, 'none'); if (settings.blindList.hasOwnProperty(userName)) { targetOverlay(trElement, 'block') seeButtonVisible(trElement) } } }) // 글 인 경우 document.querySelectorAll('#post_list > tbody > tr').forEach((trElement) => { addButtons(trElement) let targetSpan = trElement.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span span') || trElement.querySelector('td:nth-child(3) table tbody tr td:nth-child(2) span span'); let userName = targetSpan ? targetSpan.textContent.trim() : null; if (userName) { addOverlay(trElement, 'none'); if (settings.blindList.hasOwnProperty(userName)) { targetOverlay(trElement, 'block') seeButtonVisible(trElement) } } }) updateBlindListDisplay(); window.addEventListener('load', () => { observeIframe(); setInterval(fetchAndSyncRemotePage, 5000); }); })();