diff --git a/src/main/resources/static/js/comment-cards.js b/src/main/resources/static/js/comment-cards.js index 37b1dc6..63a72f0 100644 --- a/src/main/resources/static/js/comment-cards.js +++ b/src/main/resources/static/js/comment-cards.js @@ -49,6 +49,23 @@ return null; } + // 제외 단어: 쉼표/공백으로 구분. 하나라도 포함된 댓글은 카드·분석 모두에서 제외. + function getExcludeTerms() { + const raw = ($('ccExclude') && $('ccExclude').value) || ''; + return raw.split(/[,\s]+/).map(s => s.trim().toLowerCase()).filter(Boolean); + } + function excludeComments(list, terms) { + if (!terms || !terms.length) return list; + return list.filter(c => { + const t = toPlainText(c.text).toLowerCase(); + return !terms.some(term => t.includes(term)); + }); + } + function debounce(fn, ms) { + let h = null; + return function () { clearTimeout(h); h = setTimeout(fn, ms); }; + } + function applyFilterSort(cards) { const sort = $('ccSort').value; const minLikes = parseInt($('ccMinLikes').value, 10) || 0; @@ -67,7 +84,8 @@ window.renderCards = function () { const grid = $('cardGrid'); - let list = applyFilterSort(window.__all); + const base = excludeComments(window.__all, getExcludeTerms()); + let list = applyFilterSort(base); const total = list.length; const capped = total > DISPLAY_CAP; if (capped) list = list.slice(0, DISPLAY_CAP); @@ -226,7 +244,9 @@ function renderAnalytics(all) { const box = $('ccAnalytics'); - if (!all || !all.length) { box.style.display = 'none'; box.innerHTML = ''; return; } + const exTerms = getExcludeTerms(); + all = excludeComments(all || [], exTerms); + if (!all.length) { box.style.display = 'none'; box.innerHTML = ''; return; } const weighted = !!($('ccWeighted') && $('ccWeighted').checked); const tlSort = ($('ccTlSort') && $('ccTlSort').value) || 'count'; @@ -237,7 +257,8 @@ const vid = window.__videoId; let html = '
📊 분석 ' - + '전체 ' + all.length.toLocaleString() + '개 댓글 기준' + + '' + all.length.toLocaleString() + '개 댓글 기준' + + (exTerms.length ? ' · 제외어 적용' : '') + '' + '
' + '
'; @@ -434,6 +455,11 @@ $('ccUrl').addEventListener('keydown', (e) => { if (e.key === 'Enter') fetchComments(); }); ['ccSearch', 'ccSort', 'ccMinLikes', 'ccRepliesOnly'].forEach(id => $(id).addEventListener('input', () => window.renderCards())); + // 제외 단어는 카드+분석 둘 다 갱신. 대량 댓글 대비 디바운스. + $('ccExclude').addEventListener('input', debounce(() => { + window.renderCards(); + renderAnalytics(window.__all); + }, 250)); $('ccMosaic').addEventListener('click', () => { mosaicOn = !mosaicOn; $('ccMosaic').textContent = mosaicOn ? '모자이크 해제' : '전체 모자이크'; diff --git a/src/main/resources/templates/comment-cards.html b/src/main/resources/templates/comment-cards.html index 12d193f..0837085 100644 --- a/src/main/resources/templates/comment-cards.html +++ b/src/main/resources/templates/comment-cards.html @@ -93,6 +93,7 @@