From 6d4803d8e92429ef9d0335026dcb81e9aa701797 Mon Sep 17 00:00:00 2001 From: horihiro Date: Mon, 18 Mar 2024 19:26:47 +0900 Subject: [PATCH 1/3] Add title masking --- content/js/main.js | 63 +++++++++++++++++++++++++++++++++++++---- popup/css/main.css | 9 ++++++ popup/img/tab-title.svg | 5 ++++ popup/js/main.js | 17 +++++++++-- popup/main.html | 6 +++- 5 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 popup/img/tab-title.svg diff --git a/content/js/main.js b/content/js/main.js index b237636..df8c945 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -3,6 +3,7 @@ const exElmList = ['html', 'title', 'script', 'noscript', 'style', 'meta', 'link', 'head', 'textarea', '#comment']; const blurredClassName = 'tb_blurred_class'; const keepClassName = 'tb_keep_this_class'; + const originalTitleAttributeName = 'data-tb-original-title'; const maskContainerClassName = 'tb_mask_container_class'; const textLayerClassName = 'tb_mask_text_layer_class'; const inputCloneId = 'tb_input_clone'; @@ -456,10 +457,10 @@ m.forEach((n) => { if (n.classList.contains(blurredClassName) && n.classList.contains(keepClassName)) { // restore title - const originalTitle = n.getAttribute('data-tb-original-title'); + const originalTitle = n.getAttribute(originalTitleAttributeName); if (originalTitle) { n.setAttribute('title', originalTitle); - n.removeAttribute('data-tb-original-title'); + n.removeAttribute(originalTitleAttributeName); } else n.removeAttribute('title'); @@ -504,6 +505,50 @@ console.debug(`Took ${Date.now() - now} ms`) }; + const unblurTabTitle = (title) => { + if (!title) return; + if (title.getAttribute(originalTitleAttributeName)) { + title.textContent = title.getAttribute(originalTitleAttributeName); + title.removeAttribute(originalTitleAttributeName); + } + if (!w.__titleObserver) return; + w.__titleObserver.disconnect(); + delete w.__titleObserver; + }; + + const blurTabTitleCore = (pattern, target) => { + const title = target.textContent; + let result = title.match(pattern); + while (result) { + const mask = ''.padStart(result[0].length, '*'); + target.textContent = target.textContent.replace(result[0], mask); + result = target.textContent.match(pattern); + if (!target.getAttribute(originalTitleAttributeName)) { + target.setAttribute(originalTitleAttributeName, title); + } + } + }; + + const blurTabTitle = (pattern, title) => { + if (!title) return; + blurTabTitleCore(pattern, title); + if (!w.__titleObserver) { + w.__titleObserver = new MutationObserver((records) => { + w.__titleObserver.disconnect(); + title.removeAttribute(originalTitleAttributeName); + records.forEach((record) => { + blurTabTitleCore(pattern, record.target); + }); + w.__titleObserver.observe(title, { + characterData: true, childList: true + }); + }); + } + w.__titleObserver.observe(title, { + characterData: true, childList: true + }); + }; + const escapeRegExp = (str) => { return str.replace(/([\(\)\{\}\+\*\?\[\]\.\^\$\|\\])/g, '\\$1'); }; @@ -515,19 +560,25 @@ ); }; + const title = document.querySelector('title'); chrome.storage.onChanged.addListener(async (changes, area) => { if (area !== 'local') return; - const { status, keywords, mode, matchCase, showValue, blurInput } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput'])); + const { status, keywords, mode, matchCase, showValue, blurInput, blurTitle } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput', 'blurTitle'])); unblur(); + unblurTabTitle(title); if (status === 'disabled' || !keywords || keywords.trim() === '') return; - blur(keywords2RegExp(keywords, mode, !!matchCase), { showValue, blurInput }); + const pattern = keywords2RegExp(keywords, mode, !!matchCase); + blur(pattern, { showValue, blurInput }); + blurTitle && blurTabTitle(pattern, title); }); - const { status, keywords, mode, matchCase, showValue, blurInput } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput'])); + const { status, keywords, mode, matchCase, showValue, blurInput, blurTitle } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput', 'blurTitle'])); if (status === 'disabled' || !keywords || keywords.trim() === '') return; window.addEventListener('resize', () => { inputs.forEach((input) => { input.element.dispatchEvent(new InputEvent('input', { data: input.value })); }); }) - blur(keywords2RegExp(keywords, mode, !!matchCase), { showValue, blurInput }); + const pattern = keywords2RegExp(keywords, mode, !!matchCase); + blur(pattern, { showValue, blurInput }); + blurTitle && blurTabTitle(pattern, title) })(); \ No newline at end of file diff --git a/popup/css/main.css b/popup/css/main.css index 231cf19..101a93a 100644 --- a/popup/css/main.css +++ b/popup/css/main.css @@ -157,6 +157,15 @@ input[type="checkbox"]:checked.blur-input+span::before { background-image: url("../img/symbol-string.svg"); } +input[type="checkbox"].blur-title+span::before { + -webkit-mask: url("../img/tab-title.svg") no-repeat center center; + mask: url("../img/tab-title.svg") no-repeat center center; +} + +input[type="checkbox"]:checked.blur-title+span::before { + background-image: url("../img/tab-title.svg"); +} + #applyButton { margin-top: 10px; display: flex; diff --git a/popup/img/tab-title.svg b/popup/img/tab-title.svg new file mode 100644 index 0000000..91c6a7b --- /dev/null +++ b/popup/img/tab-title.svg @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/popup/js/main.js b/popup/js/main.js index c0e9c48..b3248dc 100644 --- a/popup/js/main.js +++ b/popup/js/main.js @@ -1,5 +1,5 @@ document.addEventListener('DOMContentLoaded', async (e) => { - const { status, keywords, mode, matchCase, showValue, blurInput } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput'])); + const { status, keywords, mode, matchCase, showValue, blurInput, blurTitle } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue', 'blurInput', 'blurTitle'])); const applyButton = document.querySelector('#applyButton'); const patternInput = document.querySelector('#patternInput'); @@ -8,6 +8,7 @@ document.addEventListener('DOMContentLoaded', async (e) => { const regexpCheckbox = document.querySelector('#regexpCheckbox'); const showValueCheckbox = document.querySelector('#showValueCheckbox'); const blurInputCheckbox = document.querySelector('#blurInputCheckbox'); + const blurTitleCheckbox = document.querySelector('#blurTitleCheckbox'); const _bufferTextArea = document.querySelector('#_bufferTextArea'); const COLOR_DEFAULT = getComputedStyle(_bufferTextArea).getPropertyValue('background-color'); @@ -25,6 +26,7 @@ document.addEventListener('DOMContentLoaded', async (e) => { let savedMode = false; let savedShowValue = false; let savedBlurInput = false; + let savedBlurTitle = false; let validationResults = []; let pointedRow = -1; @@ -122,7 +124,8 @@ document.addEventListener('DOMContentLoaded', async (e) => { caseCheckbox.checked === savedMatchCase && showValueCheckbox.checked === savedShowValue && regexpCheckbox.checked === savedMode && - blurInputCheckbox.checked === savedBlurInput + blurInputCheckbox.checked === savedBlurInput && + blurTitleCheckbox.checked === savedBlurTitle ); const re = /\*(\d+)( - [\d.]+px\))$/; const bgColors = validationResults.reduce((prev, curr, pos, array) => { @@ -161,6 +164,7 @@ textarea#${patternInput.id} { 'matchCase': caseCheckbox.checked, 'showValue': showValueCheckbox.checked, 'blurInput': blurInputCheckbox.checked, + 'blurTitle': blurTitleCheckbox.checked, }); patternInput.focus(); savedKeywords = patternInput.value; @@ -168,6 +172,7 @@ textarea#${patternInput.id} { savedMatchCase = caseCheckbox.checked; savedShowValue = showValueCheckbox.checked; savedBlurInput = blurInputCheckbox.checked; + savedBlurTitle = blurTitleCheckbox.checked; e.target.disabled = true; }); @@ -175,6 +180,7 @@ textarea#${patternInput.id} { caseCheckbox.disabled = showValueCheckbox.disabled = blurInputCheckbox.disabled = + blurTitleCheckbox.disabled = regexpCheckbox.disabled = patternInput.disabled = !e.target.checked; applyButton.disabled = !e.target.checked || !validationResults.every(r => r.isValid); @@ -213,6 +219,11 @@ textarea#${patternInput.id} { patternInput.focus(); }); + blurTitleCheckbox.addEventListener('change', async (e) => { + await renderBackground(); + patternInput.focus(); + }); + patternInput.addEventListener('scroll', renderBackground); patternInput.addEventListener('scroll', () => { if (patternInput.scrollLeft < textAreaPaddingLeft) patternInput.scrollLeft = 0; @@ -243,12 +254,14 @@ textarea#${patternInput.id} { savedMatchCase = caseCheckbox.checked = matchCase; savedShowValue = showValueCheckbox.checked = showValue; savedBlurInput = blurInputCheckbox.checked = blurInput; + savedBlurTitle = blurTitleCheckbox.checked = blurTitle; savedMode = regexpCheckbox.checked = mode === 'regexp'; savedKeywords = patternInput.value = keywords || ''; caseCheckbox.disabled = blurInputCheckbox.disabled = + blurTitleCheckbox.disabled = showValueCheckbox.disabled = regexpCheckbox.disabled = patternInput.disabled = diff --git a/popup/main.html b/popup/main.html index cfb2b90..ad21938 100644 --- a/popup/main.html +++ b/popup/main.html @@ -27,9 +27,13 @@
+