빠른 실행 + 차단 목록 기반 CSS + 편집 UI 포함
Od
// ==UserScript==
// @name DCInside 자짤 차단기
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 빠른 실행 + 차단 목록 기반 CSS + 편집 UI 포함
// @match *://*.dcinside.com/*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @license MIT
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
const STORAGE_KEY = 'dc_blocked_urls';
// 현재 차단 목록 가져오기
function getBlockedList() {
return JSON.parse(GM_getValue(STORAGE_KEY, '[]'));
}
// 차단 목록 저장
function saveBlockedList(list) {
GM_setValue(STORAGE_KEY, JSON.stringify(list));
}
// CSS로 직접 차단
function applyCSSBlocking() {
const list = getBlockedList();
if (list.length === 0) return;
const style = document.createElement('style');
style.id = 'dc-block-style';
style.textContent = list.map(part => `
img[src*="${part}"],
video[data-src*="${part}"],
video[src*="${part}"]
{ display: none !important; }
`).join('\n');
document.documentElement.appendChild(style);
}
// DOM 요소 숨기기 (Fallback)
function hideBlockedElements() {
const blocked = getBlockedList();
document.querySelectorAll('img, video').forEach(el => {
const src = el.src || el.getAttribute('data-src') || '';
if (blocked.some(part => src.includes(part))) {
el.style.display = 'none';
}
});
}
// 팝업 기반 편집 UI
function openBlockEditorPopup() {
const old = document.getElementById('blockEditorPopup');
if (old) old.remove();
const wrapper = document.createElement('div');
wrapper.id = 'blockEditorPopup';
wrapper.innerHTML = `
<div class="popup-box">
<h3>차단된 URL 목록 편집</h3>
<textarea id="blockEditorTextarea">${getBlockedList().join('\n')}</textarea>
<div class="popup-actions">
<button id="saveBlockedUrls">저장</button>
<button id="cancelBlockedUrls">취소</button>
</div>
</div>
`;
document.body.appendChild(wrapper);
GM_addStyle(`
#blockEditorPopup {
position: fixed;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background: #2a2a2a; /* 다크모드 대비용 진회색 */
color: #f0f0f0; /* 밝은 텍스트 */
border: 2px solid #555;
padding: 16px;
z-index: 99999;
width: 400px;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
font-family: sans-serif;
border-radius: 6px;
}
#blockEditorPopup h3 {
margin-top: 0;
font-size: 1.1em;
}
#blockEditorPopup textarea {
width: 100%;
height: 200px;
margin-top: 8px;
resize: vertical;
font-family: monospace;
background: #1e1e1e;
color: #f0f0f0;
border: 1px solid #777;
padding: 8px;
}
#blockEditorPopup .popup-actions {
margin-top: 10px;
text-align: right;
}
#blockEditorPopup button {
margin-left: 8px;
padding: 6px 12px;
background: #444;
color: #f0f0f0;
border: 1px solid #888;
border-radius: 4px;
cursor: pointer;
}
#blockEditorPopup button:hover {
background: #666;
border-color: #aaa;
}
`);
document.getElementById('saveBlockedUrls').onclick = () => {
const value = document.getElementById('blockEditorTextarea').value;
const list = value.split('\n').map(s => s.trim()).filter(s => s.length > 0);
saveBlockedList(list);
document.getElementById('dc-block-style')?.remove();
applyCSSBlocking();
hideBlockedElements();
alert('저장되었습니다!');
wrapper.remove();
};
document.getElementById('cancelBlockedUrls').onclick = () => {
wrapper.remove();
};
}
// 메뉴 등록
GM_registerMenuCommand('차단 목록 보기/편집 (팝업)', openBlockEditorPopup);
GM_registerMenuCommand('마지막 우클릭한 URL 차단하기', function () {
const url = GM_getValue('__temp_block_candidate', '');
if (!url) {
alert('이미지나 영상을 먼저 우클릭하세요.');
return;
}
const short = prompt('차단할 URL 일부를 입력하세요 (기본값: 전체 URL)', url);
if (short !== null) {
const list = getBlockedList();
const trimmed = short.trim();
if (!list.includes(trimmed)) {
list.push(trimmed);
saveBlockedList(list);
document.getElementById('dc-block-style')?.remove();
applyCSSBlocking();
hideBlockedElements();
alert('차단되었습니다: ' + trimmed);
}
}
});
// 우클릭한 URL 저장
document.addEventListener('contextmenu', function (e) {
const target = e.target;
let url = '';
if (target.tagName === 'IMG') {
url = target.src;
} else if (target.tagName === 'VIDEO') {
url = target.getAttribute('data-src') || target.src;
}
if (url) {
GM_setValue('__temp_block_candidate', url);
}
});
// 초기 실행
applyCSSBlocking();
// 동적 이미지 대응
const observer = new MutationObserver(() => hideBlockedElements());
observer.observe(document.documentElement, { childList: true, subtree: true });
// 로딩 후 한번 더 (혹시 누락된 경우)
window.addEventListener('load', hideBlockedElements);
})();