From 8f869fe76b8ab8d0096c5b9477f4e35f26e9b624 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sun, 24 Sep 2023 07:44:07 +0900 Subject: [PATCH 01/38] decrease number of added elements --- content/js/main.js | 71 +++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 08f08d7..55d1acb 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -7,15 +7,22 @@ return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; }; - const getElementsByNodeValue = (value, target) => { - const nodes = []; - Array.prototype.filter.call((target || document).childNodes, (n) => { - return n.nodeName.toLowerCase() !== 'span' || !(`${n.className}`.includes(blurredClassName)); - }).forEach((n) => { - !n.nodeValue && nodes.push(...getElementsByNodeValue(value, n)); - value.test(n.nodeValue) && nodes.push(n.parentNode); - }); - return nodes; + const getElementsByNodeValue = (pattern, target) => { + return Array.prototype.filter.call((target || document).childNodes, (n) => { + return n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName)); + }).reduce((array, n) => { + if (!n.nodeValue) { + array.push(...getElementsByNodeValue(pattern, n)); + return array; + } + const result = n.nodeValue.match(pattern); + if (!result) return array; + array.push({ + exact: result[result.index] === result.input, + node: n.parentNode + }); + return array; + }, []); }; const blurByRegExpPatterns = (patterns) => { @@ -24,28 +31,42 @@ patterns.forEach((pattern) => { console.debug(`Searching pattern ${pattern}`); getElementsByNodeValue(pattern, document.body) - .reduce((prev, n) => { - if (!prev.includes(n) - && !exElmList.includes(n.nodeName.toLowerCase()) - && Array.prototype.filter.call(n.childNodes, (c) => { + .reduce((prev, o) => { + if (!prev.includes(o) + && !exElmList.includes(o.node.nodeName.toLowerCase()) + && Array.prototype.filter.call(o.node.childNodes, (c) => { return c.nodeName === '#text' && pattern.test(c.nodeValue); }).length > 0 - && getStateOfContentEditable(n) !== 'true' - ) prev.push(n); + && getStateOfContentEditable(o.node) !== 'true' + ) prev.push(o); return prev; - }, []).forEach((n) => { - if (n.className && `${n.className}`.includes(blurredClassName)) return; - const size = Math.floor(parseFloat(getComputedStyle(n).fontSize) / 4); + }, []).forEach((o) => { + const n = o.node; + if (n.classList.contains(blurredClassName)) return; + const computedStyle = getComputedStyle(n); + const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); + + // case of that the element doesn't contain nodes except the matched keyword, + if (n.childNodes.length == 1 + && n.firstChild.nodeName === '#text' + && o.exact + && computedStyle.filter === 'none' + ) { + n.classList.add(blurredClassName); + if (size > 5) n.style.filter += ` blur(${size}px)`; + return; + } + n.childNodes.forEach((c) => { if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const referenceNode = c.nextSibling; const textArray = c.nodeValue.split(pattern); + const referenceNode = c.nextSibling; const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); c.nodeValue = textArray.shift(); textArray.forEach((t) => { const blurredSpan = document.createElement('span'); - blurredSpan.className = blurredClassName; + blurredSpan.classList.add(blurredClassName); blurredSpan.innerText = matched.shift(); if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; c.parentNode.insertBefore(blurredSpan, referenceNode); @@ -78,6 +99,16 @@ const now = Date.now(); m.forEach((n) => { + if (n.nodeName.toLowerCase() !== 'span' || n.classList.length > 1) { + // restore class + n.classList.remove(blurredClassName); + if (n.classList.length == 0) n.removeAttribute('class'); + + // restore style + n.style.filter = n.style.filter.replace(/blur\([^\)]+\)/, '').trim(); + if (n.style.length == 0) n.removeAttribute('style'); + return; + } const p = n.parentNode; n.childNodes.forEach((c) => { if (c.nodeName !== '#text') { From 81817e31bafe28451ec4a73caea4dc198fe0199f Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 27 Sep 2023 07:41:10 +0900 Subject: [PATCH 02/38] initial implmemet --- content/js/main.js | 146 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 18 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 55d1acb..32a24cb 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -11,51 +11,161 @@ return Array.prototype.filter.call((target || document).childNodes, (n) => { return n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName)); }).reduce((array, n) => { - if (!n.nodeValue) { + if (n.nodeName !== "#text") { array.push(...getElementsByNodeValue(pattern, n)); + // if (!array.some((o) => n.contains(o.node))) { + // if (!array.some((o) => n == o.node)) { + // if (!array.some((o) => n == o.node.parentNode && n.innerText.trim() === o.node.innerText.trim())) { + const result = (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) && n.innerText?.match(pattern); + if (result) { + array.push({ + splitted: true, + node: n + }); + } + // } return array; } const result = n.nodeValue.match(pattern); - if (!result) return array; - array.push({ - exact: result[result.index] === result.input, - node: n.parentNode - }); + if (result) { + array.push({ + exact: result[result.index] === result.input, + node: n.parentNode + }); + } return array; }, []); }; + const getNextTextNode = (e, root) => { + if (!e) return null; + if (e.firstChild) return e.firstChild.nodeName === '#text' ? e.firstChild : getNextTextNode(e.firstChild, root); + if (e.nextSibling) return e.nextSibling.nodeName === '#text' ? e.nextSibling : getNextTextNode(e.nextSibling, root); + + let parent = e.parentNode; + while (parent != root && parent) { + if (parent.nextSibling) return parent.nextSibling.nodeName === '#text' ? parent.nextSibling : getNextTextNode(parent.nextSibling, root); + parent = parent.parentNode; + } + return null; + } + + const getPreviousTextNode = (e, root) => { + if (!e) return null; + if (e.lastChild) return e.lastChild.nodeName === '#text' ? e.lastChild : getPreviousTextNode(e.lastChild, root); + if (e.previousSibling) return e.previousSibling.nodeName === '#text' ? e.previousSibling : getPreviousTextNode(e.previousSibling, root); + + let parent = e.parentNode; + while (parent != root && parent) { + if (parent.previousSibling) return parent.previousSibling.nodeName === '#text' ? parent.previousSibling : getPreviousTextNode(parent.previousSibling, root); + parent = parent.parentNode; + } + return null; + } + + const inchworm = (e, pattern) => { + let tail = e.firstChild.nodeName === '#text' ? e.firstChild : getNextTextNode(e.firstChild, e), head = getNextTextNode(tail, e); + let result; + do { + let str = ''; + let pos = tail; + do { + str = `${str}${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.nodeValue}`; + result = str.match(pattern); + if (result) break; + pos = getNextTextNode(pos, e); + } while (!result && pos); + head = pos; + if (!head) { + tail = getNextTextNode(head, e); + head = getNextTextNode(tail, e); + continue; + } + + str = ''; + pos = head; + do { + str = `${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.nodeValue}${str}`; + result = str.match(pattern); + if (result) break; + pos = getPreviousTextNode(pos, e); + } while (pos); + tail = pos; + if (!tail) { + tail = getNextTextNode(head, e); + head = getNextTextNode(tail, e); + continue; + } + + const blurred1 = document.createElement('span'); + blurred1.classList.add(blurredClassName); + blurred1.innerText = tail.nodeValue.slice(result.index);; + tail.nodeValue = tail.nodeValue.slice(0, result.index); + tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); + tail.parentNode.insertBefore(blurred1, tail.nextSibling); + pos = getNextTextNode(blurred1.firstChild, e); + while (pos != head) { + if (pos.nodeValue !== '') { + const span = document.createElement('span'); + span.classList.add(blurredClassName); + pos.parentNode.insertBefore(document.createTextNode(''), pos); + pos.parentNode.insertBefore(span, pos); + span.appendChild(pos); + } + pos = getNextTextNode(pos, e); + } + const blurred2 = document.createElement('span'); + const p = head.nodeValue.length - str.length + result.index + result[0].length; + blurred2.classList.add(blurredClassName); + blurred2.innerText = head.nodeValue.slice(0, p);; + head.nodeValue = head.nodeValue.slice(p); + head.parentNode.insertBefore(document.createTextNode(''), head); + head.parentNode.insertBefore(blurred2, head); + + tail = getNextTextNode(head, e); + head = getNextTextNode(tail, e); + } while (head && tail); + } + const blurByRegExpPatterns = (patterns) => { if (patterns.length === 0) return; const now = Date.now(); patterns.forEach((pattern) => { console.debug(`Searching pattern ${pattern}`); - getElementsByNodeValue(pattern, document.body) - .reduce((prev, o) => { - if (!prev.includes(o) - && !exElmList.includes(o.node.nodeName.toLowerCase()) - && Array.prototype.filter.call(o.node.childNodes, (c) => { + let array = getElementsByNodeValue(pattern, document.body); + console.debug('retrived:'); + console.debug(array); + array = array.filter((o) => { + return !exElmList.includes(o.node.nodeName.toLowerCase()) + && (Array.prototype.filter.call(o.node.childNodes, (c) => { return c.nodeName === '#text' && pattern.test(c.nodeValue); - }).length > 0 + }).length > 0 || pattern.test(o.node.innerText)) && getStateOfContentEditable(o.node) !== 'true' - ) prev.push(o); - return prev; - }, []).forEach((o) => { + }); + [...new Set(array)].sort((a) => { + return a.splitted ? 1 : -1; + }).forEach((o) => { const n = o.node; if (n.classList.contains(blurredClassName)) return; + const computedStyle = getComputedStyle(n); const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); // case of that the element doesn't contain nodes except the matched keyword, if (n.childNodes.length == 1 - && n.firstChild.nodeName === '#text' - && o.exact - && computedStyle.filter === 'none' + && n.firstChild.nodeName === '#text' + && o.exact + && computedStyle.filter === 'none' + && n.nodeName.toLowerCase() !== 'span' ) { n.classList.add(blurredClassName); if (size > 5) n.style.filter += ` blur(${size}px)`; return; } + if (o.splitted) { + inchworm(n, pattern); + return; + } n.childNodes.forEach((c) => { if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; From 175764bba400037dd17dbc0f483faf35cb29ef7c Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 27 Sep 2023 11:54:53 +0900 Subject: [PATCH 03/38] inline formatting --- content/js/main.js | 47 +++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 32a24cb..ec4b948 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -26,7 +26,7 @@ // } return array; } - const result = n.nodeValue.match(pattern); + const result = inlineFormatting(n.textContent).match(pattern); if (result) { array.push({ exact: result[result.index] === result.input, @@ -63,6 +63,15 @@ return null; } + // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#how_does_css_process_whitespace + const inlineFormatting = (str) => { + return str + .replace(/ *\n */g, '\n') // step.1 + .replace(/[\n\t]/g, ' ') // step.2&3 + .replace(/ +/g, ' ') // step.4 + .trim() // step.5 + } + const inchworm = (e, pattern) => { let tail = e.firstChild.nodeName === '#text' ? e.firstChild : getNextTextNode(e.firstChild, e), head = getNextTextNode(tail, e); let result; @@ -70,8 +79,8 @@ let str = ''; let pos = tail; do { - str = `${str}${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.nodeValue}`; - result = str.match(pattern); + str = `${str}${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.textContent}`; + result = inlineFormatting(str).match(pattern); if (result) break; pos = getNextTextNode(pos, e); } while (!result && pos); @@ -85,8 +94,8 @@ str = ''; pos = head; do { - str = `${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.nodeValue}${str}`; - result = str.match(pattern); + str = `${pos.parentNode.classList.contains(blurredClassName) ? '' : pos.textContent}${str}`; + result = inlineFormatting(str).match(pattern); if (result) break; pos = getPreviousTextNode(pos, e); } while (pos); @@ -99,13 +108,13 @@ const blurred1 = document.createElement('span'); blurred1.classList.add(blurredClassName); - blurred1.innerText = tail.nodeValue.slice(result.index);; - tail.nodeValue = tail.nodeValue.slice(0, result.index); + blurred1.textContent = tail.textContent.slice(result.index);; + tail.textContent = tail.textContent.slice(0, result.index); tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); pos = getNextTextNode(blurred1.firstChild, e); while (pos != head) { - if (pos.nodeValue !== '') { + if (pos.textContent !== '') { const span = document.createElement('span'); span.classList.add(blurredClassName); pos.parentNode.insertBefore(document.createTextNode(''), pos); @@ -115,10 +124,10 @@ pos = getNextTextNode(pos, e); } const blurred2 = document.createElement('span'); - const p = head.nodeValue.length - str.length + result.index + result[0].length; + const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length; blurred2.classList.add(blurredClassName); - blurred2.innerText = head.nodeValue.slice(0, p);; - head.nodeValue = head.nodeValue.slice(p); + blurred2.textContent = head.textContent.slice(0, p);; + head.textContent = head.textContent.slice(p); head.parentNode.insertBefore(document.createTextNode(''), head); head.parentNode.insertBefore(blurred2, head); @@ -138,7 +147,7 @@ array = array.filter((o) => { return !exElmList.includes(o.node.nodeName.toLowerCase()) && (Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.nodeValue); + return c.nodeName === '#text' && pattern.test(c.textContent); }).length > 0 || pattern.test(o.node.innerText)) && getStateOfContentEditable(o.node) !== 'true' }); @@ -168,11 +177,11 @@ } n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const textArray = c.nodeValue.split(pattern); + if (c.nodeName !== "#text" || !pattern.test(c.textContent)) return; + const textArray = c.textContent.split(pattern); const referenceNode = c.nextSibling; - const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); - c.nodeValue = textArray.shift(); + const matched = c.textContent.match(new RegExp(pattern.source, `g${pattern.flags}`)); + c.textContent = textArray.shift(); textArray.forEach((t) => { const blurredSpan = document.createElement('span'); @@ -232,14 +241,14 @@ p.insertBefore(c, n); break; } - if (textContainer.previousSibling && textContainer.nodeValue === '') { + if (textContainer.previousSibling && textContainer.textContent === '') { textContainer = textContainer.previousSibling; continue; } - textContainer.nodeValue += c.nodeValue; + textContainer.textContent += c.textContent; if (n.nextSibling?.nodeName === '#text') { - n.previousSibling.nodeValue += n.nextSibling.nodeValue; + n.previousSibling.textContent += n.nextSibling.textContent; p.removeChild(n.nextSibling); } break; From ba7f5a6b2dd278f1e7907bedbc20bf4c9ed080e8 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 27 Sep 2023 13:00:31 +0900 Subject: [PATCH 04/38] update for next release --- README.md | 4 ++++ manifest.json | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c7c545..0b5f04d 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,10 @@ If you can try a development version, the following steps are needed. # Change logs +## [0.1.3](https://github.com/horihiro/TextBlurrer-ChromeExtension/releases/tag/0.1.3) + +T.B.D. + ## [0.1.2](https://github.com/horihiro/TextBlurrer-ChromeExtension/releases/tag/0.1.2) - New features diff --git a/manifest.json b/manifest.json index 9568036..02c540f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 3, "name": "Text Blurrer", - "version": "0.1.2", - "version_name": "0.1.2", + "version": "0.1.3", + "version_name": "0.1.3", "description": "Blurring sensitive specified text/keyword.", "permissions": [ "storage" From 0677ffe5a902d08d25924e69484ef7cf3480d252 Mon Sep 17 00:00:00 2001 From: horihiro Date: Thu, 28 Sep 2023 13:00:59 +0900 Subject: [PATCH 05/38] replace `innerText` to `textContent` --- content/js/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index ec4b948..d87960a 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -16,7 +16,7 @@ // if (!array.some((o) => n.contains(o.node))) { // if (!array.some((o) => n == o.node)) { // if (!array.some((o) => n == o.node.parentNode && n.innerText.trim() === o.node.innerText.trim())) { - const result = (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) && n.innerText?.match(pattern); + const result = (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) && n.textContent?.match(pattern); if (result) { array.push({ splitted: true, @@ -148,7 +148,7 @@ return !exElmList.includes(o.node.nodeName.toLowerCase()) && (Array.prototype.filter.call(o.node.childNodes, (c) => { return c.nodeName === '#text' && pattern.test(c.textContent); - }).length > 0 || pattern.test(o.node.innerText)) + }).length > 0 || pattern.test(o.node.textContent)) && getStateOfContentEditable(o.node) !== 'true' }); [...new Set(array)].sort((a) => { @@ -186,7 +186,7 @@ textArray.forEach((t) => { const blurredSpan = document.createElement('span'); blurredSpan.classList.add(blurredClassName); - blurredSpan.innerText = matched.shift(); + blurredSpan.textContent = matched.shift(); if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; c.parentNode.insertBefore(blurredSpan, referenceNode); c.parentNode.insertBefore(document.createTextNode(t), referenceNode); From 914bb498db1f1ae9cb3a5545ea81cc80fd180cd6 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sun, 1 Oct 2023 16:54:00 +0900 Subject: [PATCH 06/38] initial implement --- content/css/blur.css | 16 ++++ content/js/main.js | 206 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 181 insertions(+), 41 deletions(-) diff --git a/content/css/blur.css b/content/css/blur.css index fd3e599..8921e89 100644 --- a/content/css/blur.css +++ b/content/css/blur.css @@ -1,3 +1,19 @@ .blurred { filter: blur(5px); +} + +.mask { + border: none; + overflow: hidden; +} + +#__inputClone, .mask, .backgroundLayer, .textLayer { + position: absolute; + top: 0px; + left: 0px; + border: none; +} + +#__inputClone { + visibility: hidden; } \ No newline at end of file diff --git a/content/js/main.js b/content/js/main.js index 55d1acb..2f6e4db 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -6,6 +6,7 @@ if (element.contentEditable && element.contentEditable !== 'inherit') return element.contentEditable; return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; }; + const inputs = []; const getElementsByNodeValue = (pattern, target) => { return Array.prototype.filter.call((target || document).childNodes, (n) => { @@ -31,53 +32,160 @@ patterns.forEach((pattern) => { console.debug(`Searching pattern ${pattern}`); getElementsByNodeValue(pattern, document.body) - .reduce((prev, o) => { - if (!prev.includes(o) - && !exElmList.includes(o.node.nodeName.toLowerCase()) - && Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.nodeValue); - }).length > 0 - && getStateOfContentEditable(o.node) !== 'true' - ) prev.push(o); - return prev; - }, []).forEach((o) => { - const n = o.node; - if (n.classList.contains(blurredClassName)) return; - const computedStyle = getComputedStyle(n); - const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); - - // case of that the element doesn't contain nodes except the matched keyword, - if (n.childNodes.length == 1 - && n.firstChild.nodeName === '#text' - && o.exact - && computedStyle.filter === 'none' - ) { - n.classList.add(blurredClassName); - if (size > 5) n.style.filter += ` blur(${size}px)`; - return; - } + .reduce((prev, o) => { + if (!prev.includes(o) + && !exElmList.includes(o.node.nodeName.toLowerCase()) + && Array.prototype.filter.call(o.node.childNodes, (c) => { + return c.nodeName === '#text' && pattern.test(c.nodeValue); + }).length > 0 + && getStateOfContentEditable(o.node) !== 'true' + ) prev.push(o); + return prev; + }, []).forEach((o) => { + const n = o.node; + if (n.classList.contains(blurredClassName)) return; + const computedStyle = getComputedStyle(n); + const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); + + // case of that the element doesn't contain nodes except the matched keyword, + if (n.childNodes.length == 1 + && n.firstChild.nodeName === '#text' + && o.exact + && computedStyle.filter === 'none' + ) { + n.classList.add(blurredClassName); + if (size > 5) n.style.filter += ` blur(${size}px)`; + return; + } - n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const textArray = c.nodeValue.split(pattern); - const referenceNode = c.nextSibling; - const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); - c.nodeValue = textArray.shift(); - - textArray.forEach((t) => { - const blurredSpan = document.createElement('span'); - blurredSpan.classList.add(blurredClassName); - blurredSpan.innerText = matched.shift(); - if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; - c.parentNode.insertBefore(blurredSpan, referenceNode); - c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + n.childNodes.forEach((c) => { + if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; + const textArray = c.nodeValue.split(pattern); + const referenceNode = c.nextSibling; + const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); + c.nodeValue = textArray.shift(); + + textArray.forEach((t) => { + const blurredSpan = document.createElement('span'); + blurredSpan.classList.add(blurredClassName); + blurredSpan.innerText = matched.shift(); + if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; + c.parentNode.insertBefore(blurredSpan, referenceNode); + c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + }); }); - }); - }) + }) }); + [...document.querySelectorAll('input')].reduce((inputs, input) => { + const inputObj = (() => { + const array = inputs.filter((inputObj) => inputObj.element == input); + if (array.length > 0) return array[0]; + inputs.push({ element: input, masks: [] }); + return inputs[inputs.length - 1]; + })(); + if (inputObj.inputHandler) return inputs; + inputObj.inputHandler = inputOnInput.bind({ patterns }); + input.addEventListener('input', inputObj.inputHandler); + input.addEventListener('focus', inputOnFocus); + input.addEventListener('blur', inputOnBlur); + input.dispatchEvent(new InputEvent('input', { data: input.value })); + return inputs; + }, inputs); console.debug(`Took ${Date.now() - now} ms`) }; + const inputOnInput = function (e) { + const input = e.target; + this.patterns.forEach((pattern) => { + const inputObj = inputs.filter(i => i.element == input)[0]; + if (!inputObj) return; + + while (inputObj.masks.length > 0) { + inputObj.masks[0].parentNode && inputObj.masks[0].parentNode.removeChild(inputObj.masks[0]); + inputObj.masks.shift(); + } + inputObj.masks.length = 0; + + if (!pattern.test(input.value)) return; + console.log(input.value); + + const clone = document.querySelector("#__inputClone"); + const inputStyle = getComputedStyle(input); + while (clone.firstChild) { + clone.removeChild(clone.firstChild); + } + // clone.textContent = inputObj.element.value; + for (let s in inputStyle) { + if (!isNaN(parseInt(s))) continue; + if (!['display', 'position', 'visibility', 'top', 'left'].includes(s)) clone.style.setProperty(s, inputStyle.getPropertyValue(s)); + } + + const inputBoundingBox = input.getBoundingClientRect(); + const size = Math.floor(parseFloat(inputStyle.fontSize) / 4); + + const textArray = input.value.split(pattern); + const matched = input.value.match(new RegExp(pattern.source, `g${pattern.flags}`)); + clone.appendChild(document.createTextNode(textArray.shift())); + const referenceNode = clone.lastChild.nextSibling; + const parentBoundingBox = clone.getBoundingClientRect(); + textArray.forEach((t) => { + const blurredSpan = document.createElement('span'); + blurredSpan.classList.add(blurredClassName); + blurredSpan.textContent = matched.shift(); + if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; + clone.insertBefore(blurredSpan, referenceNode); + clone.insertBefore(document.createTextNode(t), referenceNode); + + const mask = document.createElement('div'); + mask.classList.add('mask'); + mask.appendChild(document.createElement('div')); + mask.lastChild.classList.add('backgroundLayer'); + mask.lastChild.style.setProperty('width', '100%'); + mask.lastChild.style.setProperty('height', '100%'); + mask.appendChild(document.createElement('div')); + mask.lastChild.classList.add('textLayer'); + mask.lastChild.textContent = blurredSpan.textContent; + mask.lastChild.style.setProperty('width', '100%'); + mask.lastChild.style.setProperty('height', '100%'); + input.parentNode.appendChild(mask); + + const blurredBoundingBox = blurredSpan.getBoundingClientRect(); + + for (let s in inputStyle) { + if (!isNaN(parseInt(s))) continue; + if (!['position', 'filter', 'margin', 'padding', 'border', 'top', 'left', 'overflow', 'height', 'width', 'outline'].includes(s)) { + mask.style.setProperty(s, inputStyle.getPropertyValue(s)); + } + } + + mask.style.setProperty('left', `${blurredBoundingBox.left - parentBoundingBox.left + inputBoundingBox.left}px`); + mask.style.setProperty('top', `${blurredBoundingBox.top - parentBoundingBox.top + inputBoundingBox.top}px`); + + const maskBoundingBox = mask.getBoundingClientRect(); + mask.style.setProperty('width', `${inputBoundingBox.width + inputBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > maskBoundingBox.left + blurredBoundingBox.width ? blurredBoundingBox.width : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) : 0}px`); + //mask.style.setProperty('width', `${blurredBoundingBox.width}px`); + mask.style.setProperty('height', `${blurredBoundingBox.height}px`); + mask.style.setProperty('z-index', `${parseInt(inputStyle.getPropertyValue) + 1}`); + mask.style.setProperty('border', 'none'); + mask.querySelector('.mask>.backgroundLayer').style.setProperty('background-color', inputStyle.getPropertyValue('background-color')); + mask.style.setProperty('display', 'none'); + + inputObj.masks.push(mask); + }); + }); + } + const inputOnFocus = (e) => { + const input = e.target; + const inputObj = inputs.filter(i => i.element == input)[0]; + if (!inputObj) return; + inputObj.masks.forEach(m => m.style.setProperty('display', 'none')); + } + const inputOnBlur = (e) => { + const input = e.target; + const inputObj = inputs.filter(i => i.element == input)[0]; + if (!inputObj) return; + inputObj.masks.forEach(m => m.style.setProperty('display', '')); + } const blur = (keywords) => { if (w.__observer) return; w.__observer = new MutationObserver(() => { @@ -88,12 +196,28 @@ subtree: true, characterData: true }); + const inputClone = (() => { + return document.querySelector('#__inputClone') || document.createElement('div'); + })(); + if (!inputClone.parentNode) { + inputClone.id = '__inputClone'; + document.body.appendChild(inputClone); + } + blurByRegExpPatterns(keywords); }; const unblur = () => { if (!w.__observer) return; w.__observer.disconnect(); delete w.__observer + + inputs.forEach((inputObj) => { + inputObj.element.removeEventListener('input', inputObj.inputHandler); + inputObj.element.removeEventListener('focus', inputOnFocus); + inputObj.element.removeEventListener('blur', inputOnBlur); + inputObj.inputHandler = null; + }); + const m = w.document.querySelectorAll(`.${blurredClassName}`); if (m.length === 0) return; From 23d5b3fa55daa7529b3103a1f95b70bc4aa90666 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sun, 1 Oct 2023 18:57:52 +0900 Subject: [PATCH 07/38] fix background color --- content/js/main.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 2f6e4db..34c8f54 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -138,10 +138,10 @@ const mask = document.createElement('div'); mask.classList.add('mask'); - mask.appendChild(document.createElement('div')); - mask.lastChild.classList.add('backgroundLayer'); - mask.lastChild.style.setProperty('width', '100%'); - mask.lastChild.style.setProperty('height', '100%'); + // mask.appendChild(document.createElement('div')); + // mask.lastChild.classList.add('backgroundLayer'); + // mask.lastChild.style.setProperty('width', '100%'); + // mask.lastChild.style.setProperty('height', '100%'); mask.appendChild(document.createElement('div')); mask.lastChild.classList.add('textLayer'); mask.lastChild.textContent = blurredSpan.textContent; @@ -167,13 +167,20 @@ mask.style.setProperty('height', `${blurredBoundingBox.height}px`); mask.style.setProperty('z-index', `${parseInt(inputStyle.getPropertyValue) + 1}`); mask.style.setProperty('border', 'none'); - mask.querySelector('.mask>.backgroundLayer').style.setProperty('background-color', inputStyle.getPropertyValue('background-color')); + + mask.style.setProperty('background-color', getBackgroundColorAlongDOMTree(input)); mask.style.setProperty('display', 'none'); inputObj.masks.push(mask); }); }); } + const getBackgroundColorAlongDOMTree = (element) => { + const computedStyle = getComputedStyle(element); + return (!/(?:^| )rgba *\( *\d+ *, *\d+ *, *\d+ *, *0 *\)(?:$| )/.test(computedStyle.getPropertyValue('background-color'))) + ? computedStyle.getPropertyValue('background-color') + : getBackgroundColorAlongDOMTree(element.parentNode); + } const inputOnFocus = (e) => { const input = e.target; const inputObj = inputs.filter(i => i.element == input)[0]; From dfdb6d4d1bb607df28fb87c20168acecf248ed1d Mon Sep 17 00:00:00 2001 From: horihiro Date: Sun, 1 Oct 2023 19:03:55 +0900 Subject: [PATCH 08/38] remove comment lines --- content/js/main.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 34c8f54..d3455d4 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -114,7 +114,6 @@ while (clone.firstChild) { clone.removeChild(clone.firstChild); } - // clone.textContent = inputObj.element.value; for (let s in inputStyle) { if (!isNaN(parseInt(s))) continue; if (!['display', 'position', 'visibility', 'top', 'left'].includes(s)) clone.style.setProperty(s, inputStyle.getPropertyValue(s)); @@ -138,10 +137,6 @@ const mask = document.createElement('div'); mask.classList.add('mask'); - // mask.appendChild(document.createElement('div')); - // mask.lastChild.classList.add('backgroundLayer'); - // mask.lastChild.style.setProperty('width', '100%'); - // mask.lastChild.style.setProperty('height', '100%'); mask.appendChild(document.createElement('div')); mask.lastChild.classList.add('textLayer'); mask.lastChild.textContent = blurredSpan.textContent; @@ -162,8 +157,12 @@ mask.style.setProperty('top', `${blurredBoundingBox.top - parentBoundingBox.top + inputBoundingBox.top}px`); const maskBoundingBox = mask.getBoundingClientRect(); - mask.style.setProperty('width', `${inputBoundingBox.width + inputBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > maskBoundingBox.left + blurredBoundingBox.width ? blurredBoundingBox.width : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) : 0}px`); - //mask.style.setProperty('width', `${blurredBoundingBox.width}px`); + mask.style.setProperty('width', `${ + inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > blurredBoundingBox.width + ? blurredBoundingBox.width + : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 + ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) + : 0}px`); mask.style.setProperty('height', `${blurredBoundingBox.height}px`); mask.style.setProperty('z-index', `${parseInt(inputStyle.getPropertyValue) + 1}`); mask.style.setProperty('border', 'none'); From 8dac5a63e28499fd47cd8c972315a336c4d40f3d Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 3 Oct 2023 08:55:16 +0900 Subject: [PATCH 09/38] fix blurring login in input --- content/css/blur.css | 4 ++- content/js/main.js | 58 +++++++++++++++++++++++++++----------------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/content/css/blur.css b/content/css/blur.css index 8921e89..b378d48 100644 --- a/content/css/blur.css +++ b/content/css/blur.css @@ -7,11 +7,13 @@ overflow: hidden; } -#__inputClone, .mask, .backgroundLayer, .textLayer { +#__inputClone, .mask, .textLayer { position: absolute; top: 0px; left: 0px; border: none; + overflow: hidden; + white-space: nowrap; } #__inputClone { diff --git a/content/js/main.js b/content/js/main.js index d3455d4..9a7c00a 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -80,7 +80,7 @@ const inputObj = (() => { const array = inputs.filter((inputObj) => inputObj.element == input); if (array.length > 0) return array[0]; - inputs.push({ element: input, masks: [] }); + inputs.push({ element: input, masks: {} }); return inputs[inputs.length - 1]; })(); if (inputObj.inputHandler) return inputs; @@ -97,26 +97,35 @@ const inputOnInput = function (e) { const input = e.target; this.patterns.forEach((pattern) => { - const inputObj = inputs.filter(i => i.element == input)[0]; + const inputObj = inputs.filter(i => i.element == input )[0]; if (!inputObj) return; - while (inputObj.masks.length > 0) { - inputObj.masks[0].parentNode && inputObj.masks[0].parentNode.removeChild(inputObj.masks[0]); - inputObj.masks.shift(); + const patternStr = `/${pattern.source}/${pattern.flags}`; + if (!inputObj.masks[patternStr]) inputObj.masks[patternStr] = []; + while (inputObj.masks[patternStr].length > 0) { + inputObj.masks[patternStr][0].parentNode && inputObj.masks[patternStr][0].parentNode.removeChild(inputObj.masks[patternStr][0]); + inputObj.masks[patternStr].shift(); } - inputObj.masks.length = 0; + inputObj.masks[patternStr].length = 0; if (!pattern.test(input.value)) return; - console.log(input.value); - const clone = document.querySelector("#__inputClone"); + // const clone = document.querySelector("#__inputClone"); + const clone = (() => { + return document.querySelector('#__inputClone') || document.createElement('div'); + })(); + if (!clone.parentNode) { + clone.id = '__inputClone'; + document.body.appendChild(clone); + } + clone.textCotent = ''; const inputStyle = getComputedStyle(input); while (clone.firstChild) { clone.removeChild(clone.firstChild); } for (let s in inputStyle) { if (!isNaN(parseInt(s))) continue; - if (!['display', 'position', 'visibility', 'top', 'left'].includes(s)) clone.style.setProperty(s, inputStyle.getPropertyValue(s)); + if (!['display', 'position', 'visibility', 'top', 'left', 'overflow', 'white-space'].includes(s)) clone.style.setProperty(s, inputStyle.getPropertyValue(s)); } const inputBoundingBox = input.getBoundingClientRect(); @@ -153,24 +162,25 @@ } } - mask.style.setProperty('left', `${blurredBoundingBox.left - parentBoundingBox.left + inputBoundingBox.left}px`); - mask.style.setProperty('top', `${blurredBoundingBox.top - parentBoundingBox.top + inputBoundingBox.top}px`); - + console.debug(blurredBoundingBox); + console.debug(inputStyle.getPropertyValue('left'), inputStyle.getPropertyValue('top')) + mask.style.setProperty('left', `${blurredSpan.offsetLeft + input.offsetLeft + parseFloat(inputStyle.getPropertyValue('border-left-width'))}px`); + mask.style.setProperty('top', `${input.offsetTop + input.offsetHeight - blurredSpan.offsetHeight - parseFloat(inputStyle.getPropertyValue('border-bottom-width'))}px`); + console.debug(mask.style.getPropertyValue('left'), mask.style.getPropertyValue('top')) const maskBoundingBox = mask.getBoundingClientRect(); - mask.style.setProperty('width', `${ - inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > blurredBoundingBox.width - ? blurredBoundingBox.width - : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 - ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) - : 0}px`); + mask.style.setProperty('width', `${inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > blurredBoundingBox.width + ? blurredBoundingBox.width + : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 + ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) + : 0}px`); mask.style.setProperty('height', `${blurredBoundingBox.height}px`); mask.style.setProperty('z-index', `${parseInt(inputStyle.getPropertyValue) + 1}`); mask.style.setProperty('border', 'none'); mask.style.setProperty('background-color', getBackgroundColorAlongDOMTree(input)); - mask.style.setProperty('display', 'none'); + e.isTrusted && mask.style.setProperty('display', 'none'); - inputObj.masks.push(mask); + inputObj.masks[patternStr].push(mask); }); }); } @@ -184,13 +194,17 @@ const input = e.target; const inputObj = inputs.filter(i => i.element == input)[0]; if (!inputObj) return; - inputObj.masks.forEach(m => m.style.setProperty('display', 'none')); + for (let p in inputObj.masks) { + inputObj.masks[p].forEach(m => m.style.setProperty('display', 'none')); + } } const inputOnBlur = (e) => { const input = e.target; const inputObj = inputs.filter(i => i.element == input)[0]; if (!inputObj) return; - inputObj.masks.forEach(m => m.style.setProperty('display', '')); + for (let p in inputObj.masks) { + inputObj.masks[p].forEach(m => m.style.setProperty('display', '')); + } } const blur = (keywords) => { if (w.__observer) return; From 76faeb02d9605b469625a927f89e08193f310c94 Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 3 Oct 2023 17:42:28 +0900 Subject: [PATCH 10/38] fix logic --- README.md | 3 ++- content/js/main.js | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b5f04d..08ffd90 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ If you can try a development version, the following steps are needed. ## [0.1.3](https://github.com/horihiro/TextBlurrer-ChromeExtension/releases/tag/0.1.3) -T.B.D. + - Bug fixes + - Improve performance by change blurring logic ## [0.1.2](https://github.com/horihiro/TextBlurrer-ChromeExtension/releases/tag/0.1.2) diff --git a/content/js/main.js b/content/js/main.js index 55d1acb..f4239df 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -2,6 +2,7 @@ const w = window; const exElmList = ['html', 'title', 'script', 'noscript', 'style', 'meta', 'link', 'head', 'textarea']; const blurredClassName = 'blurred'; + const keepClassName = '__keep_this'; const getStateOfContentEditable = (element) => { if (element.contentEditable && element.contentEditable !== 'inherit') return element.contentEditable; return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; @@ -53,6 +54,7 @@ && computedStyle.filter === 'none' ) { n.classList.add(blurredClassName); + n.classList.add(keepClassName); if (size > 5) n.style.filter += ` blur(${size}px)`; return; } @@ -99,9 +101,10 @@ const now = Date.now(); m.forEach((n) => { - if (n.nodeName.toLowerCase() !== 'span' || n.classList.length > 1) { + if (n.classList.contains(blurredClassName) && n.classList.contains(keepClassName)) { // restore class n.classList.remove(blurredClassName); + n.classList.remove(keepClassName); if (n.classList.length == 0) n.removeAttribute('class'); // restore style From c3d587c48b5f743d49bea86bfb916761f992b596 Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 3 Oct 2023 17:49:33 +0900 Subject: [PATCH 11/38] remove some `console`s --- content/js/main.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index db613c9..023577f 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -164,11 +164,8 @@ } } - console.debug(blurredBoundingBox); - console.debug(inputStyle.getPropertyValue('left'), inputStyle.getPropertyValue('top')) mask.style.setProperty('left', `${blurredSpan.offsetLeft + input.offsetLeft + parseFloat(inputStyle.getPropertyValue('border-left-width'))}px`); mask.style.setProperty('top', `${input.offsetTop + input.offsetHeight - blurredSpan.offsetHeight - parseFloat(inputStyle.getPropertyValue('border-bottom-width'))}px`); - console.debug(mask.style.getPropertyValue('left'), mask.style.getPropertyValue('top')) const maskBoundingBox = mask.getBoundingClientRect(); mask.style.setProperty('width', `${inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > blurredBoundingBox.width ? blurredBoundingBox.width From f24c6c94426eb37292c08b97a70dd2c3f58ff29a Mon Sep 17 00:00:00 2001 From: horihiro Date: Fri, 6 Oct 2023 15:55:49 +0900 Subject: [PATCH 12/38] implement for shadow dom --- content/js/main.js | 144 ++++++++++++++++++++++++++++----------------- manifest.json | 3 - 2 files changed, 89 insertions(+), 58 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index f4239df..24f1689 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -8,12 +8,15 @@ return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; }; - const getElementsByNodeValue = (pattern, target) => { - return Array.prototype.filter.call((target || document).childNodes, (n) => { + const getElementsByNodeValue = (pattern, target, keywords) => { + return Array.prototype.filter.call((target || document.body).childNodes, (n) => { return n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName)); }).reduce((array, n) => { if (!n.nodeValue) { - array.push(...getElementsByNodeValue(pattern, n)); + if (n.shadowRoot) { + blur(keywords, n.shadowRoot); + } + array.push(...getElementsByNodeValue(pattern, n, keywords)); return array; } const result = n.nodeValue.match(pattern); @@ -26,77 +29,108 @@ }, []); }; - const blurByRegExpPatterns = (patterns) => { + const blurByRegExpPatterns = (patterns, target) => { if (patterns.length === 0) return; const now = Date.now(); - patterns.forEach((pattern) => { + patterns.forEach((pattern, _, array) => { console.debug(`Searching pattern ${pattern}`); - getElementsByNodeValue(pattern, document.body) - .reduce((prev, o) => { - if (!prev.includes(o) - && !exElmList.includes(o.node.nodeName.toLowerCase()) - && Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.nodeValue); - }).length > 0 - && getStateOfContentEditable(o.node) !== 'true' - ) prev.push(o); - return prev; - }, []).forEach((o) => { - const n = o.node; - if (n.classList.contains(blurredClassName)) return; - const computedStyle = getComputedStyle(n); - const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); + getElementsByNodeValue(pattern, target || document.body, array) + .reduce((prev, o) => { + if (!prev.includes(o) + && !exElmList.includes(o.node.nodeName.toLowerCase()) + && Array.prototype.filter.call(o.node.childNodes, (c) => { + return c.nodeName === '#text' && pattern.test(c.nodeValue); + }).length > 0 + && getStateOfContentEditable(o.node) !== 'true' + ) prev.push(o); + return prev; + }, []).forEach((o) => { + const n = o.node; + if (n.classList.contains(blurredClassName)) return; + const computedStyle = getComputedStyle(n); + const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); - // case of that the element doesn't contain nodes except the matched keyword, - if (n.childNodes.length == 1 - && n.firstChild.nodeName === '#text' - && o.exact - && computedStyle.filter === 'none' - ) { - n.classList.add(blurredClassName); - n.classList.add(keepClassName); - if (size > 5) n.style.filter += ` blur(${size}px)`; - return; - } + // case of that the element doesn't contain nodes except the matched keyword, + if (n.childNodes.length == 1 + && n.firstChild.nodeName === '#text' + && o.exact + && computedStyle.filter === 'none' + ) { + n.classList.add(blurredClassName); + n.classList.add(keepClassName); + if (size > 5) n.style.filter += ` blur(${size}px)`; + return; + } - n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const textArray = c.nodeValue.split(pattern); - const referenceNode = c.nextSibling; - const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); - c.nodeValue = textArray.shift(); + n.childNodes.forEach((c) => { + if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; + const textArray = c.nodeValue.split(pattern); + const referenceNode = c.nextSibling; + const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); + c.nodeValue = textArray.shift(); - textArray.forEach((t) => { - const blurredSpan = document.createElement('span'); - blurredSpan.classList.add(blurredClassName); - blurredSpan.innerText = matched.shift(); - if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; - c.parentNode.insertBefore(blurredSpan, referenceNode); - c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + textArray.forEach((t) => { + const blurredSpan = document.createElement('span'); + blurredSpan.classList.add(blurredClassName); + blurredSpan.innerText = matched.shift(); + if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; + c.parentNode.insertBefore(blurredSpan, referenceNode); + c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + }); }); - }); - }) + }) }); console.debug(`Took ${Date.now() - now} ms`) }; - const blur = (keywords) => { - if (w.__observer) return; - w.__observer = new MutationObserver(() => { - blurByRegExpPatterns(keywords); - }); - w.__observer.observe(w.document, { + const observedNodes = []; + const blur = (keywords, target) => { + const observed = target || document.body; + if (observedNodes.includes(observed)) return; + + const style = document.createElement('style'); + style.innerHTML = `.${blurredClassName} { + filter: blur(5px); +}`; + style.id = '__blurring_style'; + !observed.querySelector(`#${style.id}`) && observed.appendChild(style); + observedNodes.push(observed); + if (!w.__observer) { + w.__observer = new MutationObserver((records) => { + const targets = records.reduce((targets, record) => { + const isContained = targets.some((target) => { + return target.contains(record.target); + }); + if (isContained) return targets; + const array = targets.reduce((prev, target) => { + if (!record.target.contains(target) && target != record.target) { + prev.push(target) + } + return prev; + }, []); + array.push(record.target); + return array; + }, []); + targets.forEach(target => blurByRegExpPatterns(keywords, target)); + }); + } + w.__observer.observe(observed, { childList: true, subtree: true, characterData: true }); - blurByRegExpPatterns(keywords); + blurByRegExpPatterns(keywords, observed); }; const unblur = () => { if (!w.__observer) return; w.__observer.disconnect(); - delete w.__observer - const m = w.document.querySelectorAll(`.${blurredClassName}`); + delete w.__observer; + const m = observedNodes.reduce((array, target) => { + array.push(...target.querySelectorAll(`.${blurredClassName}`)); + return array; + }, []); + observedNodes.length = 0; + // const m = w.document.querySelectorAll(`.${blurredClassName}`); if (m.length === 0) return; const now = Date.now(); diff --git a/manifest.json b/manifest.json index 02c540f..f8afbd1 100644 --- a/manifest.json +++ b/manifest.json @@ -20,9 +20,6 @@ ], "js": [ "content/js/main.js" - ], - "css": [ - "content/css/blur.css" ] } ] From 586a2c09ea8dfbbf618e817d0e1787e92dc90104 Mon Sep 17 00:00:00 2001 From: horihiro Date: Fri, 6 Oct 2023 15:58:52 +0900 Subject: [PATCH 13/38] change classname --- content/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 24f1689..4c846bc 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -1,8 +1,8 @@ (async () => { const w = window; const exElmList = ['html', 'title', 'script', 'noscript', 'style', 'meta', 'link', 'head', 'textarea']; - const blurredClassName = 'blurred'; - const keepClassName = '__keep_this'; + const blurredClassName = '__text_blurrer_blurred_class'; + const keepClassName = '__text_blurrer_keep_this_class'; const getStateOfContentEditable = (element) => { if (element.contentEditable && element.contentEditable !== 'inherit') return element.contentEditable; return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; From 32c91facb6ff98425157d0c8c535c261dd688a8b Mon Sep 17 00:00:00 2001 From: horihiro Date: Fri, 6 Oct 2023 20:06:42 +0900 Subject: [PATCH 14/38] fix conditions --- content/js/main.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index f4239df..a3977f1 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -48,9 +48,8 @@ const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); // case of that the element doesn't contain nodes except the matched keyword, - if (n.childNodes.length == 1 - && n.firstChild.nodeName === '#text' - && o.exact + if (o.exact + && Array.prototype.every.call(n.childNodes, c => c.nodeName === '#text') && computedStyle.filter === 'none' ) { n.classList.add(blurredClassName); From 3bc39283c4a10a02a5c387fefc339386a950a6cd Mon Sep 17 00:00:00 2001 From: horihiro Date: Fri, 6 Oct 2023 20:10:28 +0900 Subject: [PATCH 15/38] fix indent --- content/js/main.js | 78 +++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index a3977f1..1b4b8ef 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -32,49 +32,49 @@ patterns.forEach((pattern) => { console.debug(`Searching pattern ${pattern}`); getElementsByNodeValue(pattern, document.body) - .reduce((prev, o) => { - if (!prev.includes(o) - && !exElmList.includes(o.node.nodeName.toLowerCase()) - && Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.nodeValue); - }).length > 0 - && getStateOfContentEditable(o.node) !== 'true' - ) prev.push(o); - return prev; - }, []).forEach((o) => { - const n = o.node; - if (n.classList.contains(blurredClassName)) return; - const computedStyle = getComputedStyle(n); - const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); + .reduce((prev, o) => { + if (!prev.includes(o) + && !exElmList.includes(o.node.nodeName.toLowerCase()) + && Array.prototype.filter.call(o.node.childNodes, (c) => { + return c.nodeName === '#text' && pattern.test(c.nodeValue); + }).length > 0 + && getStateOfContentEditable(o.node) !== 'true' + ) prev.push(o); + return prev; + }, []).forEach((o) => { + const n = o.node; + if (n.classList.contains(blurredClassName)) return; + const computedStyle = getComputedStyle(n); + const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); - // case of that the element doesn't contain nodes except the matched keyword, - if (o.exact - && Array.prototype.every.call(n.childNodes, c => c.nodeName === '#text') - && computedStyle.filter === 'none' - ) { - n.classList.add(blurredClassName); - n.classList.add(keepClassName); - if (size > 5) n.style.filter += ` blur(${size}px)`; - return; - } + // case of that the element doesn't contain nodes except the matched keyword, + if (o.exact + && Array.prototype.every.call(n.childNodes, c => c.nodeName === '#text') + && computedStyle.filter === 'none' + ) { + n.classList.add(blurredClassName); + n.classList.add(keepClassName); + if (size > 5) n.style.filter += ` blur(${size}px)`; + return; + } - n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const textArray = c.nodeValue.split(pattern); - const referenceNode = c.nextSibling; - const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); - c.nodeValue = textArray.shift(); + n.childNodes.forEach((c) => { + if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; + const textArray = c.nodeValue.split(pattern); + const referenceNode = c.nextSibling; + const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); + c.nodeValue = textArray.shift(); - textArray.forEach((t) => { - const blurredSpan = document.createElement('span'); - blurredSpan.classList.add(blurredClassName); - blurredSpan.innerText = matched.shift(); - if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; - c.parentNode.insertBefore(blurredSpan, referenceNode); - c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + textArray.forEach((t) => { + const blurredSpan = document.createElement('span'); + blurredSpan.classList.add(blurredClassName); + blurredSpan.innerText = matched.shift(); + if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; + c.parentNode.insertBefore(blurredSpan, referenceNode); + c.parentNode.insertBefore(document.createTextNode(t), referenceNode); + }); }); - }); - }) + }) }); console.debug(`Took ${Date.now() - now} ms`) }; From fd7f19bdadb85b27a29d0d49e8eb7f8dc4fbe8ee Mon Sep 17 00:00:00 2001 From: horihiro Date: Fri, 6 Oct 2023 20:16:51 +0900 Subject: [PATCH 16/38] fix indent --- content/js/main.js | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 454f348..e657f1d 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -191,50 +191,9 @@ if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; c.parentNode.insertBefore(blurredSpan, referenceNode); c.parentNode.insertBefore(document.createTextNode(t), referenceNode); - getElementsByNodeValue(pattern, document.body) - .reduce((prev, o) => { - if (!prev.includes(o) - && !exElmList.includes(o.node.nodeName.toLowerCase()) - && Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.nodeValue); - }).length > 0 - && getStateOfContentEditable(o.node) !== 'true' - ) prev.push(o); - return prev; - }, []).forEach((o) => { - const n = o.node; - if (n.classList.contains(blurredClassName)) return; - const computedStyle = getComputedStyle(n); - const size = Math.floor(parseFloat(computedStyle.fontSize) / 4); - - // case of that the element doesn't contain nodes except the matched keyword, - if (o.exact - && Array.prototype.every.call(n.childNodes, c => c.nodeName === '#text') - && computedStyle.filter === 'none' - ) { - n.classList.add(blurredClassName); - n.classList.add(keepClassName); - if (size > 5) n.style.filter += ` blur(${size}px)`; - return; - } - - n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.nodeValue)) return; - const textArray = c.nodeValue.split(pattern); - const referenceNode = c.nextSibling; - const matched = c.nodeValue.match(new RegExp(pattern.source, `g${pattern.flags}`)); - c.nodeValue = textArray.shift(); - - textArray.forEach((t) => { - const blurredSpan = document.createElement('span'); - blurredSpan.classList.add(blurredClassName); - blurredSpan.innerText = matched.shift(); - if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; - c.parentNode.insertBefore(blurredSpan, referenceNode); - c.parentNode.insertBefore(document.createTextNode(t), referenceNode); - }); }); - }) + }); + }) }); console.debug(`Took ${Date.now() - now} ms`) }; From b904916027822693b27b0e81717b533378eec0d4 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sat, 7 Oct 2023 09:32:13 +0900 Subject: [PATCH 17/38] fix bug --- content/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/js/main.js b/content/js/main.js index e657f1d..2ec0c78 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -114,7 +114,7 @@ tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); pos = getNextTextNode(blurred1.firstChild, e); - while (pos != head) { + while (pos && pos != head) { if (pos.textContent !== '') { const span = document.createElement('span'); span.classList.add(blurredClassName); From 2f2a749240494a6439fef08a4be38d054a7a83b0 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sat, 7 Oct 2023 10:41:02 +0900 Subject: [PATCH 18/38] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 08ffd90..510cd1a 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ If you can try a development version, the following steps are needed. - Bug fixes - Improve performance by change blurring logic + - Blur keywords in shadow DOM ## [0.1.2](https://github.com/horihiro/TextBlurrer-ChromeExtension/releases/tag/0.1.2) From a462f4d562528fa389f47b14945871bf645e8474 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sat, 7 Oct 2023 12:57:35 +0900 Subject: [PATCH 19/38] fix bug --- content/js/main.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index dd1a215..ce97d19 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -1,6 +1,6 @@ (async () => { const w = window; - const exElmList = ['html', 'title', 'script', 'noscript', 'style', 'meta', 'link', 'head', 'textarea']; + const exElmList = ['html', 'title', 'script', 'noscript', 'style', 'meta', 'link', 'head', 'textarea', '#comment']; const blurredClassName = '__text_blurrer_blurred_class'; const keepClassName = '__text_blurrer_keep_this_class'; const getStateOfContentEditable = (element) => { @@ -16,10 +16,7 @@ if (n.shadowRoot) { blur(keywords, n.shadowRoot); } - array.push(...getElementsByNodeValue(pattern, n)); - // if (!array.some((o) => n.contains(o.node))) { - // if (!array.some((o) => n == o.node)) { - // if (!array.some((o) => n == o.node.parentNode && n.innerText.trim() === o.node.innerText.trim())) { + array.push(...getElementsByNodeValue(pattern, n, keywords)); const result = (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) && n.textContent?.match(pattern); if (result) { array.push({ @@ -244,7 +241,6 @@ return array; }, []); observedNodes.length = 0; - // const m = w.document.querySelectorAll(`.${blurredClassName}`); if (m.length === 0) return; const now = Date.now(); @@ -269,7 +265,7 @@ let textContainer = n.previousSibling; do { - if (textContainer.nodeName !== '#text') { + if (!textContainer || textContainer.nodeName !== '#text') { p.insertBefore(c, n); break; } From 8e86416918c60740075dd5d3b5723183332bbd60 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sat, 7 Oct 2023 18:29:11 +0900 Subject: [PATCH 20/38] fix bug for splitted keywords --- content/js/main.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index ce97d19..7efedad 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -10,20 +10,24 @@ const getElementsByNodeValue = (pattern, target, keywords) => { return Array.prototype.filter.call((target || document.body).childNodes, (n) => { - return n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName)); + return !exElmList.includes(n.nodeName.toLowerCase()) && (n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName))); }).reduce((array, n) => { if (n.nodeName !== "#text") { if (n.shadowRoot) { blur(keywords, n.shadowRoot); } array.push(...getElementsByNodeValue(pattern, n, keywords)); - const result = (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) && n.textContent?.match(pattern); - if (result) { - array.push({ + const nodearray = array.map(o => o.node); + if (inlineFormatting(Array.prototype.filter.call(n.childNodes, (c) => c.nodeName === '#text').map(c => c.nodeValue).join('')).match(pattern)) { + !nodearray.includes(n) && array.push({ + node: n + }); + } else if (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source) && inlineFormatting(n.textContent).match(pattern)) { + !nodearray.includes(n) && array.push({ splitted: true, node: n }); - } + } return array; } const result = inlineFormatting(n.textContent).match(pattern); @@ -65,11 +69,12 @@ // https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace#how_does_css_process_whitespace const inlineFormatting = (str) => { - return str + return str ? str .replace(/ *\n */g, '\n') // step.1 .replace(/[\n\t]/g, ' ') // step.2&3 .replace(/ +/g, ' ') // step.4 .trim() // step.5 + : '' } const inchworm = (e, pattern) => { @@ -124,7 +129,7 @@ pos = getNextTextNode(pos, e); } const blurred2 = document.createElement('span'); - const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length; + const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + head.textContent.replace(/^([ \n\t]+).*/, '$1').length; blurred2.classList.add(blurredClassName); blurred2.textContent = head.textContent.slice(0, p);; head.textContent = head.textContent.slice(p); @@ -142,14 +147,13 @@ patterns.forEach((pattern, _, array) => { console.debug(`Searching pattern ${pattern}`); const targetObjects = getElementsByNodeValue(pattern, target || document.body, array).filter((o) => { - return !exElmList.includes(o.node.nodeName.toLowerCase()) - && (Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(c.textContent); - }).length > 0 || pattern.test(o.node.textContent)) + return (Array.prototype.filter.call(o.node.childNodes, (c) => { + return c.nodeName === '#text' && pattern.test(inlineFormatting(c.textContent)); + }).length > 0 || pattern.test(inlineFormatting(o.node.textContent))) && getStateOfContentEditable(o.node) !== 'true' }); [...new Set(targetObjects)].sort((a) => { - return a.splitted ? 1 : -1; + return !a.exact ? 1 : a.splitted ? 1 : -1; }).forEach((o) => { const n = o.node; if (n.classList.contains(blurredClassName)) return; @@ -161,7 +165,6 @@ if (o.exact && Array.prototype.every.call(n.childNodes, c => c.nodeName === '#text') && computedStyle.filter === 'none' - && n.nodeName.toLowerCase() !== 'span' ) { n.classList.add(blurredClassName); n.classList.add(keepClassName); From a5f2c28872f4d0c231c2f970e2f41384de71d4c0 Mon Sep 17 00:00:00 2001 From: horihiro Date: Sun, 8 Oct 2023 08:57:06 +0900 Subject: [PATCH 21/38] fix mask/clone input bugs --- content/js/main.js | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 4f1adc8..61a92bb 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -16,8 +16,6 @@ #${inputCloneId}, .${maskContainerClassName}, .${textLayerClassName} { position: absolute!important; - top: 0px!important; - left: 0px!important; border: none!important; overflow: hidden!important; white-space: nowrap!important; @@ -25,6 +23,7 @@ #${inputCloneId} { visibility: hidden!important; + white-space-collapse: preserve!important; }`; const getStateOfContentEditable = (element) => { if (element.contentEditable && element.contentEditable !== 'inherit') return element.contentEditable; @@ -32,6 +31,15 @@ }; const inputs = []; + const getTextContentRecursive = (target) => { + const textContent = Array.prototype.reduce.call(target.childNodes, (allTextContent, node) => { + if (exElmList.includes(node.nodeName.toLowerCase())) return allTextContent; + if (node.nodeName === '#text') return `${allTextContent}${node.textContent}`; + return `${allTextContent}${getTextContentRecursive(node)}`; + }, ''); + return textContent; + }; + const getElementsByNodeValue = (pattern, target, keywords) => { return Array.prototype.filter.call((target || document.body).childNodes, (n) => { return !exElmList.includes(n.nodeName.toLowerCase()) && (n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName))); @@ -46,12 +54,15 @@ !nodearray.includes(n) && array.push({ node: n }); - } else if (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source) && inlineFormatting(n.textContent).match(pattern)) { - !nodearray.includes(n) && array.push({ - splitted: true, - node: n - }); - } + } else { + const textContent = inlineFormatting(getTextContentRecursive(n)); + if (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source) && textContent.match(pattern)) { + !nodearray.includes(n) && array.push({ + splitted: true, + node: n + }); + } + } return array; } const result = inlineFormatting(n.textContent).match(pattern); @@ -135,10 +146,12 @@ continue; } + const reStartWithSpaces = /^(\s+).*/; const blurred1 = document.createElement('span'); + const numOfLeftSpacesInTail = reStartWithSpaces.test(tail.textContent) ? tail.textContent.replace(reStartWithSpaces, '$1').length : 0; blurred1.classList.add(blurredClassName); - blurred1.textContent = tail.textContent.slice(result.index);; - tail.textContent = tail.textContent.slice(0, result.index); + blurred1.textContent = tail.textContent.slice(result.index + numOfLeftSpacesInTail);; + tail.textContent = tail.textContent.slice(0, result.index + numOfLeftSpacesInTail); tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); pos = getNextTextNode(blurred1.firstChild, e); @@ -153,7 +166,8 @@ pos = getNextTextNode(pos, e); } const blurred2 = document.createElement('span'); - const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + head.textContent.replace(/^([ \n\t]+).*/, '$1').length; + const numOfLeftSpacesInHead = reStartWithSpaces.test(head.textContent) ? head.textContent.replace(reStartWithSpaces, '$1').length : 0; + const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + numOfLeftSpacesInHead; blurred2.classList.add(blurredClassName); blurred2.textContent = head.textContent.slice(0, p);; head.textContent = head.textContent.slice(p); @@ -172,8 +186,8 @@ console.debug(`Searching pattern ${pattern}`); const targetObjects = getElementsByNodeValue(pattern, target || document.body, array).filter((o) => { return (Array.prototype.filter.call(o.node.childNodes, (c) => { - return c.nodeName === '#text' && pattern.test(inlineFormatting(c.textContent)); - }).length > 0 || pattern.test(inlineFormatting(o.node.textContent))) + return c.nodeName === '#text' && pattern.test(inlineFormatting(c.textContent)); + }).length > 0 || pattern.test(inlineFormatting(o.node.textContent))) && getStateOfContentEditable(o.node) !== 'true' }); [...new Set(targetObjects)].sort((a) => { From 67c04cf969c09912406403cd5ff88ced4bdd8e5a Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 10 Oct 2023 20:46:52 +0900 Subject: [PATCH 22/38] support for continuous white spaces --- content/js/main.js | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 7efedad..47a41f9 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -8,6 +8,15 @@ return element.parentNode ? getStateOfContentEditable(element.parentNode) : ''; }; + const getTextContentRecursive = (target, options) => { + const textContent = !target.childNodes ? target.textContent : Array.prototype.reduce.call(target.childNodes, (allTextContent, node) => { + if (options?.exElmList?.includes(node.nodeName.toLowerCase()) || options?.exNodes?.includes(node)) return allTextContent; + if (node.nodeName === '#text') return `${allTextContent}${node.textContent}`; + return `${allTextContent}${getTextContentRecursive(node, options)}`; + }, ''); + return textContent; + }; + const getElementsByNodeValue = (pattern, target, keywords) => { return Array.prototype.filter.call((target || document.body).childNodes, (n) => { return !exElmList.includes(n.nodeName.toLowerCase()) && (n.nodeName.toLowerCase() !== 'span' || !(n.classList.contains(blurredClassName))); @@ -18,12 +27,22 @@ } array.push(...getElementsByNodeValue(pattern, n, keywords)); const nodearray = array.map(o => o.node); - if (inlineFormatting(Array.prototype.filter.call(n.childNodes, (c) => c.nodeName === '#text').map(c => c.nodeValue).join('')).match(pattern)) { + let result = inlineFormatting(Array.prototype.filter.call(n.childNodes, (c) => c.nodeName === '#text').map(c => c.nodeValue).join('')).match(pattern); + if (result) { !nodearray.includes(n) && array.push({ + keyword: result[0], node: n }); - } else if (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source) && inlineFormatting(n.textContent).match(pattern)) { + return array; + } + const textContent = nodearray.every((node) => { + return !n.contains(node); + }) ? n.textContent : getTextContentRecursive(n, {exNodes: nodearray}); + if (pattern.source.length <= 1 || /^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) return array; + result = inlineFormatting(textContent).match(pattern); + if (result) { !nodearray.includes(n) && array.push({ + keyword: result[0], splitted: true, node: n }); @@ -33,6 +52,7 @@ const result = inlineFormatting(n.textContent).match(pattern); if (result) { array.push({ + keyword: result[0], exact: result[result.index] === result.input, node: n.parentNode }); @@ -176,11 +196,12 @@ return; } + const reKeyword = new RegExp(escapeRegExp(o.keyword).replace(/ +/, '\\s+')); n.childNodes.forEach((c) => { - if (c.nodeName !== "#text" || !pattern.test(c.textContent)) return; - const textArray = c.textContent.split(pattern); + if (c.nodeName !== "#text" || !reKeyword.test(c.textContent)) return; + const textArray = c.textContent.split(reKeyword); const referenceNode = c.nextSibling; - const matched = c.textContent.match(new RegExp(pattern.source, `g${pattern.flags}`)); + const matched = c.textContent.match(new RegExp(reKeyword.source, `g${reKeyword.flags}`)); c.textContent = textArray.shift(); textArray.forEach((t) => { From abbd5f9228ef172d2c4c5799eb28a9bb32f043f9 Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 10 Oct 2023 21:10:48 +0900 Subject: [PATCH 23/38] fix getting textContent from elements including `script` etc --- content/js/main.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 47a41f9..b756345 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -10,7 +10,7 @@ const getTextContentRecursive = (target, options) => { const textContent = !target.childNodes ? target.textContent : Array.prototype.reduce.call(target.childNodes, (allTextContent, node) => { - if (options?.exElmList?.includes(node.nodeName.toLowerCase()) || options?.exNodes?.includes(node)) return allTextContent; + if (options?.exclusives?.nodeNames?.includes(node.nodeName.toLowerCase()) || options?.exclusives?.nodes?.includes(node)) return allTextContent; if (node.nodeName === '#text') return `${allTextContent}${node.textContent}`; return `${allTextContent}${getTextContentRecursive(node, options)}`; }, ''); @@ -35,9 +35,7 @@ }); return array; } - const textContent = nodearray.every((node) => { - return !n.contains(node); - }) ? n.textContent : getTextContentRecursive(n, {exNodes: nodearray}); + const textContent = getTextContentRecursive(n, {exclusives: {nodes: nodearray, nodeNames: exElmList}}); if (pattern.source.length <= 1 || /^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) return array; result = inlineFormatting(textContent).match(pattern); if (result) { From ed742f7a6860084bc5495b407162a75562d89e4f Mon Sep 17 00:00:00 2001 From: horihiro Date: Tue, 10 Oct 2023 21:12:55 +0900 Subject: [PATCH 24/38] resolve conflict --- content/js/main.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index ee06c03..99db7a4 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -31,15 +31,6 @@ }; const inputs = []; - const getTextContentRecursive = (target) => { - const textContent = Array.prototype.reduce.call(target.childNodes, (allTextContent, node) => { - if (exElmList.includes(node.nodeName.toLowerCase())) return allTextContent; - if (node.nodeName === '#text') return `${allTextContent}${node.textContent}`; - return `${allTextContent}${getTextContentRecursive(node)}`; - }, ''); - return textContent; - }; - const getTextContentRecursive = (target, options) => { const textContent = !target.childNodes ? target.textContent : Array.prototype.reduce.call(target.childNodes, (allTextContent, node) => { if (options?.exclusives?.nodeNames?.includes(node.nodeName.toLowerCase()) || options?.exclusives?.nodes?.includes(node)) return allTextContent; @@ -65,17 +56,6 @@ keyword: result[0], node: n }); -<<<<<<< HEAD - } else { - const textContent = inlineFormatting(getTextContentRecursive(n)); - if (pattern.source.length > 1 && !/^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source) && textContent.match(pattern)) { - !nodearray.includes(n) && array.push({ - splitted: true, - node: n - }); - } - } -======= return array; } const textContent = getTextContentRecursive(n, {exclusives: {nodes: nodearray, nodeNames: exElmList}}); @@ -88,7 +68,6 @@ node: n }); } ->>>>>>> bump-0.1.3 return array; } const result = inlineFormatting(n.textContent).match(pattern); @@ -240,11 +219,8 @@ inchworm(n, pattern); return; } -<<<<<<< HEAD -======= const reKeyword = new RegExp(escapeRegExp(o.keyword).replace(/ +/, '\\s+')); ->>>>>>> bump-0.1.3 n.childNodes.forEach((c) => { if (c.nodeName !== "#text" || !reKeyword.test(c.textContent)) return; const textArray = c.textContent.split(reKeyword); From e066609b37262d7ddb9ab2d3e04c18f29f27495c Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 08:04:34 +0900 Subject: [PATCH 25/38] fix bug of input with padding style --- content/js/main.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 99db7a4..e2e4852 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -325,12 +325,13 @@ } mask.style.setProperty('left', `${blurredSpan.offsetLeft + input.offsetLeft + parseFloat(inputStyle.getPropertyValue('border-left-width'))}px`); - mask.style.setProperty('top', `${input.offsetTop + input.offsetHeight - blurredSpan.offsetHeight - parseFloat(inputStyle.getPropertyValue('border-bottom-width'))}px`); + mask.style.setProperty('top', `${input.offsetTop + input.offsetHeight - blurredSpan.offsetHeight - parseFloat(inputStyle.getPropertyValue('border-bottom-width')) - parseFloat(inputStyle.getPropertyValue('padding-bottom'))}px`); const maskBoundingBox = mask.getBoundingClientRect(); - mask.style.setProperty('width', `${inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > blurredBoundingBox.width + const tmpWidth = inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('border-left-width')) - 1; + mask.style.setProperty('width', `${tmpWidth > blurredBoundingBox.width ? blurredBoundingBox.width - : inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) > 0 - ? inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('padding-left')) + : tmpWidth > 0 + ? tmpWidth : 0}px`); mask.style.setProperty('height', `${blurredBoundingBox.height}px`); mask.style.setProperty('z-index', `${parseInt(inputStyle.getPropertyValue) + 1}`); From 8dd88147f9b691471faadc4fe98ac426d14a82e4 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 08:25:27 +0900 Subject: [PATCH 26/38] remove masking nodes for inputs on unblur --- content/js/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index e2e4852..2acf7ab 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -416,12 +416,12 @@ w.__observer.disconnect(); delete w.__observer - inputs.forEach((inputObj) => { - inputObj.element.removeEventListener('input', inputObj.inputHandler); - inputObj.element.removeEventListener('focus', inputOnFocus); - inputObj.element.removeEventListener('blur', inputOnBlur); - inputObj.inputHandler = null; + document.querySelectorAll(`.${maskContainerClassName}`).forEach((mask) => { + mask.parentNode.removeChild(mask); }); + const inputClone = document.querySelector(`#${inputCloneId}`); + inputClone && inputClone.parentNode.removeChild(inputClone); + inputs.length = 0; const m = observedNodes.reduce((array, target) => { array.push(...target.querySelectorAll(`.${blurredClassName}`)); From c5f1b6a9bcc44beb77568497e94a848eaa48fe35 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 08:30:24 +0900 Subject: [PATCH 27/38] reblur input nodes on window resize --- content/js/main.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/js/main.js b/content/js/main.js index 2acf7ab..1b04f1c 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -497,5 +497,10 @@ }); const { status, keywords, mode, matchCase } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase'])); if (status === 'disabled') return; + window.addEventListener('resize', () => { + inputs.forEach((input) => { + input.element.dispatchEvent(new InputEvent('input', { data: input.value })); + }); + }) blur(str2RegExpArray(keywords, mode, !!matchCase)); })(); \ No newline at end of file From 3d78a1e6795dbcda2caeee6193cc93ce7c6495f9 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 10:02:18 +0900 Subject: [PATCH 28/38] remove unnecessary nodes on unblur --- content/js/main.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 1b04f1c..aa6c15e 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -6,6 +6,7 @@ const maskContainerClassName = 'tb_mask_container_class'; const textLayerClassName = 'tb_mask_text_layer_class'; const inputCloneId = 'tb_input_clone'; + const globalStyleId = '__blurring_style'; const globalStyle = `.${blurredClassName} { filter: blur(5px)!important; } @@ -327,7 +328,7 @@ mask.style.setProperty('left', `${blurredSpan.offsetLeft + input.offsetLeft + parseFloat(inputStyle.getPropertyValue('border-left-width'))}px`); mask.style.setProperty('top', `${input.offsetTop + input.offsetHeight - blurredSpan.offsetHeight - parseFloat(inputStyle.getPropertyValue('border-bottom-width')) - parseFloat(inputStyle.getPropertyValue('padding-bottom'))}px`); const maskBoundingBox = mask.getBoundingClientRect(); - const tmpWidth = inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('border-left-width')) - 1; + const tmpWidth = inputBoundingBox.width + inputBoundingBox.left - maskBoundingBox.left - parseFloat(inputStyle.getPropertyValue('border-left-width')); mask.style.setProperty('width', `${tmpWidth > blurredBoundingBox.width ? blurredBoundingBox.width : tmpWidth > 0 @@ -373,7 +374,7 @@ const style = document.createElement('style'); style.innerHTML = globalStyle; - style.id = '__blurring_style'; + style.id = globalStyleId; !observed.querySelector(`#${style.id}`) && (observed == document.body ? document.head : observed).appendChild(style); observedNodes.push(observed); if (!w.__observer) { @@ -416,14 +417,21 @@ w.__observer.disconnect(); delete w.__observer - document.querySelectorAll(`.${maskContainerClassName}`).forEach((mask) => { - mask.parentNode.removeChild(mask); + inputs.map(i => i.masks).forEach((masks) => { + for(let pattern in masks) { + masks[pattern].forEach((mask) => { + mask.parentNode.removeChild(mask); + }) + } }); - const inputClone = document.querySelector(`#${inputCloneId}`); - inputClone && inputClone.parentNode.removeChild(inputClone); inputs.length = 0; const m = observedNodes.reduce((array, target) => { + const globalStyle = target.querySelector(`#${globalStyleId}`); + globalStyle && globalStyle.parentNode.removeChild(globalStyle); + const inputClone = target.querySelector(`#${inputCloneId}`); + inputClone && inputClone.parentNode.removeChild(inputClone); + array.push(...target.querySelectorAll(`.${blurredClassName}`)); return array; }, []); From 12ef892c6d846e5d38daa6f1ff00fc4bc2525ec9 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 13:28:03 +0900 Subject: [PATCH 29/38] remove unnecessary code --- content/js/main.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index b756345..58526ac 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -27,17 +27,9 @@ } array.push(...getElementsByNodeValue(pattern, n, keywords)); const nodearray = array.map(o => o.node); - let result = inlineFormatting(Array.prototype.filter.call(n.childNodes, (c) => c.nodeName === '#text').map(c => c.nodeValue).join('')).match(pattern); - if (result) { - !nodearray.includes(n) && array.push({ - keyword: result[0], - node: n - }); - return array; - } const textContent = getTextContentRecursive(n, {exclusives: {nodes: nodearray, nodeNames: exElmList}}); if (pattern.source.length <= 1 || /^(?:\.|(?:\\[^\\])|(?:\[[^\]]+\]))(?:\?|\*|\+|\{,?1\}|\{1,(?:\d+)?\})?$/.test(pattern.source)) return array; - result = inlineFormatting(textContent).match(pattern); + const result = inlineFormatting(textContent).match(pattern); if (result) { !nodearray.includes(n) && array.push({ keyword: result[0], From e0803ae6b22f6449f9b908d9b21f3c001c599879 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 15:23:53 +0900 Subject: [PATCH 30/38] add eventhandling on mouseover and click --- content/js/main.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/content/js/main.js b/content/js/main.js index 32e7283..2498bbb 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -307,6 +307,10 @@ mask.lastChild.style.setProperty('width', '100%'); mask.lastChild.style.setProperty('height', '100%'); input.parentNode.appendChild(mask); + mask.lastChild.setAttribute('title', blurredSpan.textContent); + mask.addEventListener('click', () => { + input.focus(); + }) const blurredBoundingBox = blurredSpan.getBoundingClientRect(); From 850a658502baa836c7ef29cbfd98b7aa6ddf965d Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 15:27:39 +0900 Subject: [PATCH 31/38] add event handling on mouseover --- content/js/main.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 58526ac..9759620 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -87,7 +87,7 @@ : '' } - const inchworm = (e, pattern) => { + const inchworm = (e, pattern, keyword) => { let tail = e.firstChild.nodeName === '#text' ? e.firstChild : getNextTextNode(e.firstChild, e), head = getNextTextNode(tail, e); let result; do { @@ -124,6 +124,7 @@ const blurred1 = document.createElement('span'); blurred1.classList.add(blurredClassName); blurred1.textContent = tail.textContent.slice(result.index);; + blurred1.setAttribute('title', keyword); tail.textContent = tail.textContent.slice(0, result.index); tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); @@ -132,6 +133,7 @@ if (pos.textContent !== '') { const span = document.createElement('span'); span.classList.add(blurredClassName); + span.setAttribute('title', keyword); pos.parentNode.insertBefore(document.createTextNode(''), pos); pos.parentNode.insertBefore(span, pos); span.appendChild(pos); @@ -142,6 +144,7 @@ const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + head.textContent.replace(/^([ \n\t]+).*/, '$1').length; blurred2.classList.add(blurredClassName); blurred2.textContent = head.textContent.slice(0, p);; + blurred2.setAttribute('title', keyword); head.textContent = head.textContent.slice(p); head.parentNode.insertBefore(document.createTextNode(''), head); head.parentNode.insertBefore(blurred2, head); @@ -178,11 +181,12 @@ ) { n.classList.add(blurredClassName); n.classList.add(keepClassName); + n.setAttribute('title', o.keyword); if (size > 5) n.style.filter += ` blur(${size}px)`; return; } if (o.splitted) { - inchworm(n, pattern); + inchworm(n, pattern, o.keyword); return; } @@ -198,6 +202,7 @@ const blurredSpan = document.createElement('span'); blurredSpan.classList.add(blurredClassName); blurredSpan.textContent = matched.shift(); + blurredSpan.setAttribute('title', o.keyword); if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; c.parentNode.insertBefore(blurredSpan, referenceNode); c.parentNode.insertBefore(document.createTextNode(t), referenceNode); From a11456d9cbb88a79ea8927c60793a32a4804d633 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 15:51:42 +0900 Subject: [PATCH 32/38] handle white spaces --- content/js/main.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/content/js/main.js b/content/js/main.js index 9759620..3ae80fb 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -121,11 +121,13 @@ continue; } + const reStartWithSpaces = /^(\s+).*/; const blurred1 = document.createElement('span'); + const numOfLeftSpacesInTail = reStartWithSpaces.test(tail.textContent) ? tail.textContent.replace(reStartWithSpaces, '$1').length : 0; blurred1.classList.add(blurredClassName); - blurred1.textContent = tail.textContent.slice(result.index);; + blurred1.textContent = tail.textContent.slice(result.index + numOfLeftSpacesInTail); blurred1.setAttribute('title', keyword); - tail.textContent = tail.textContent.slice(0, result.index); + tail.textContent = tail.textContent.slice(0, result.index + numOfLeftSpacesInTail); tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); pos = getNextTextNode(blurred1.firstChild, e); @@ -141,9 +143,10 @@ pos = getNextTextNode(pos, e); } const blurred2 = document.createElement('span'); - const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + head.textContent.replace(/^([ \n\t]+).*/, '$1').length; + const numOfLeftSpacesInHead = reStartWithSpaces.test(head.textContent) ? head.textContent.replace(reStartWithSpaces, '$1').length : 0; + const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + numOfLeftSpacesInHead; blurred2.classList.add(blurredClassName); - blurred2.textContent = head.textContent.slice(0, p);; + blurred2.textContent = head.textContent.slice(0, p); blurred2.setAttribute('title', keyword); head.textContent = head.textContent.slice(p); head.parentNode.insertBefore(document.createTextNode(''), head); From 66c2ebd15b3e6ce906501f21217724a0dd5f8998 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 16:34:23 +0900 Subject: [PATCH 33/38] remove unused variables --- content/js/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/content/js/main.js b/content/js/main.js index a3cc71f..22cbf49 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -295,7 +295,6 @@ const matched = input.value.match(new RegExp(pattern.source, `g${pattern.flags}`)); clone.appendChild(document.createTextNode(textArray.shift())); const referenceNode = clone.lastChild.nextSibling; - const parentBoundingBox = clone.getBoundingClientRect(); textArray.forEach((t) => { const blurredSpan = document.createElement('span'); blurredSpan.classList.add(blurredClassName); From 6c69219bcddfa386fa3df9c727ef762e20ad4a33 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 19:29:56 +0900 Subject: [PATCH 34/38] fix attributions fo cc4.0 --- CREDITS.txt | 3 ++- popup/img/ATTRIBUTIONS.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CREDITS.txt b/CREDITS.txt index 3ae7637..d820d41 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -1,4 +1,5 @@ Image: "Codicons" -Image: Microsoft +Author: Microsoft +Web Site: https://github.com/microsoft/vscode-codicons License: Creative Commons Attribution 4.0 International License License Details: https://creativecommons.org/licenses/by/4.0/ diff --git a/popup/img/ATTRIBUTIONS.txt b/popup/img/ATTRIBUTIONS.txt index 3ae7637..d820d41 100644 --- a/popup/img/ATTRIBUTIONS.txt +++ b/popup/img/ATTRIBUTIONS.txt @@ -1,4 +1,5 @@ Image: "Codicons" -Image: Microsoft +Author: Microsoft +Web Site: https://github.com/microsoft/vscode-codicons License: Creative Commons Attribution 4.0 International License License Details: https://creativecommons.org/licenses/by/4.0/ From 702db82e34f99a67db7177403e358b5d920d8952 Mon Sep 17 00:00:00 2001 From: horihiro Date: Wed, 11 Oct 2023 19:49:41 +0900 Subject: [PATCH 35/38] add show-value mode --- content/js/main.js | 30 +++++++++++++++--------------- popup/css/main.css | 9 +++++++++ popup/img/comment.svg | 1 + popup/js/main.js | 19 ++++++++++++++++--- popup/main.html | 4 ++++ 5 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 popup/img/comment.svg diff --git a/content/js/main.js b/content/js/main.js index 3ae80fb..d78e24d 100644 --- a/content/js/main.js +++ b/content/js/main.js @@ -87,7 +87,7 @@ : '' } - const inchworm = (e, pattern, keyword) => { + const inchworm = (e, pattern, keyword, options) => { let tail = e.firstChild.nodeName === '#text' ? e.firstChild : getNextTextNode(e.firstChild, e), head = getNextTextNode(tail, e); let result; do { @@ -126,7 +126,7 @@ const numOfLeftSpacesInTail = reStartWithSpaces.test(tail.textContent) ? tail.textContent.replace(reStartWithSpaces, '$1').length : 0; blurred1.classList.add(blurredClassName); blurred1.textContent = tail.textContent.slice(result.index + numOfLeftSpacesInTail); - blurred1.setAttribute('title', keyword); + options?.showValue && blurred1.setAttribute('title', keyword); tail.textContent = tail.textContent.slice(0, result.index + numOfLeftSpacesInTail); tail.parentNode.insertBefore(document.createTextNode(''), tail.nextSibling); tail.parentNode.insertBefore(blurred1, tail.nextSibling); @@ -135,7 +135,7 @@ if (pos.textContent !== '') { const span = document.createElement('span'); span.classList.add(blurredClassName); - span.setAttribute('title', keyword); + options?.showValue && span.setAttribute('title', keyword); pos.parentNode.insertBefore(document.createTextNode(''), pos); pos.parentNode.insertBefore(span, pos); span.appendChild(pos); @@ -147,7 +147,7 @@ const p = head.textContent.trim().length - inlineFormatting(str).length + result.index + result[0].length + numOfLeftSpacesInHead; blurred2.classList.add(blurredClassName); blurred2.textContent = head.textContent.slice(0, p); - blurred2.setAttribute('title', keyword); + options?.showValue && blurred2.setAttribute('title', keyword); head.textContent = head.textContent.slice(p); head.parentNode.insertBefore(document.createTextNode(''), head); head.parentNode.insertBefore(blurred2, head); @@ -157,7 +157,7 @@ } while (head && tail); } - const blurByRegExpPatterns = (patterns, target) => { + const blurByRegExpPatterns = (patterns, options, target) => { if (patterns.length === 0) return; const now = Date.now(); patterns.forEach((pattern, _, array) => { @@ -184,12 +184,12 @@ ) { n.classList.add(blurredClassName); n.classList.add(keepClassName); - n.setAttribute('title', o.keyword); + options?.showValue && n.setAttribute('title', o.keyword); if (size > 5) n.style.filter += ` blur(${size}px)`; return; } if (o.splitted) { - inchworm(n, pattern, o.keyword); + inchworm(n, pattern, o.keyword, options); return; } @@ -205,7 +205,7 @@ const blurredSpan = document.createElement('span'); blurredSpan.classList.add(blurredClassName); blurredSpan.textContent = matched.shift(); - blurredSpan.setAttribute('title', o.keyword); + options?.showValue && blurredSpan.setAttribute('title', o.keyword); if (size > 5) blurredSpan.style.filter = `blur(${size}px)`; c.parentNode.insertBefore(blurredSpan, referenceNode); c.parentNode.insertBefore(document.createTextNode(t), referenceNode); @@ -217,7 +217,7 @@ }; const observedNodes = []; - const blur = (keywords, target) => { + const blur = (keywords, options, target) => { const observed = target || document.body; if (observedNodes.includes(observed)) return; @@ -244,7 +244,7 @@ array.push(record.target); return array; }, []); - targets.forEach(target => blurByRegExpPatterns(keywords, target)); + targets.forEach(target => blurByRegExpPatterns(keywords, options, target)); }); } w.__observer.observe(observed, { @@ -252,7 +252,7 @@ subtree: true, characterData: true }); - blurByRegExpPatterns(keywords, observed); + blurByRegExpPatterns(keywords, options, observed); }; const unblur = () => { if (!w.__observer) return; @@ -324,12 +324,12 @@ chrome.storage.onChanged.addListener(async (changes, area) => { if (area !== 'local') return; - const { status, keywords, mode, matchCase } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase'])); + const { status, keywords, mode, matchCase, showValue } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue'])); unblur(); if (status === 'disabled') return; - blur(str2RegExpArray(keywords, mode, !!matchCase)); + blur(str2RegExpArray(keywords, mode, !!matchCase), {showValue}); }); - const { status, keywords, mode, matchCase } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase'])); + const { status, keywords, mode, matchCase, showValue } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue'])); if (status === 'disabled') return; - blur(str2RegExpArray(keywords, mode, !!matchCase)); + blur(str2RegExpArray(keywords, mode, !!matchCase), {showValue}); })(); \ No newline at end of file diff --git a/popup/css/main.css b/popup/css/main.css index f8e9999..dfc7800 100644 --- a/popup/css/main.css +++ b/popup/css/main.css @@ -139,6 +139,15 @@ input[type="checkbox"]:checked.case-sensitive+span::before { background-image: url("../img/case-sensitive.svg"); } +input[type="checkbox"].show-value+span::before { + -webkit-mask: url("../img/comment.svg") no-repeat center center; + mask: url("../img/comment.svg") no-repeat center center; +} + +input[type="checkbox"]:checked.show-value+span::before { + background-image: url("../img/comment.svg"); +} + #applyButton { margin-top: 10px; display: flex; diff --git a/popup/img/comment.svg b/popup/img/comment.svg new file mode 100644 index 0000000..de59ff2 --- /dev/null +++ b/popup/img/comment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/popup/js/main.js b/popup/js/main.js index a734d02..d367617 100644 --- a/popup/js/main.js +++ b/popup/js/main.js @@ -1,11 +1,12 @@ document.addEventListener('DOMContentLoaded', async (e) => { - const { status, keywords, mode, matchCase } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase'])); + const { status, keywords, mode, matchCase, showValue } = (await chrome.storage.local.get(['status', 'keywords', 'mode', 'matchCase', 'showValue'])); const applyButton = document.querySelector('#applyButton'); const patternInput = document.querySelector('#patternInput'); const statusCheckbox = document.querySelector('#statusCheckbox'); const caseCheckbox = document.querySelector('#caseCheckbox'); const regexpCheckbox = document.querySelector('#regexpCheckbox'); + const showValueCheckbox = document.querySelector('#showValueCheckbox'); const _bufferTextArea = document.querySelector('#_bufferTextArea'); const COLOR_DEFAULT = getComputedStyle(_bufferTextArea).getPropertyValue('background-color'); @@ -21,6 +22,7 @@ document.addEventListener('DOMContentLoaded', async (e) => { let savedKeywords = ''; let savedMatchCase = false; let savedMode = false; + let savedShowValue = false; let validationResults = []; let pointedRow = -1; @@ -112,7 +114,7 @@ document.addEventListener('DOMContentLoaded', async (e) => { applyButton.disabled = false; } validationResults = await validateLines(lines, !regexpCheckbox.checked); - applyButton.disabled = !validationResults.every(r => r.isValid) || (patternInput.value === savedKeywords && caseCheckbox.checked === savedMatchCase && regexpCheckbox.checked === savedMode); + applyButton.disabled = !validationResults.every(r => r.isValid) || (patternInput.value === savedKeywords && caseCheckbox.checked === savedMatchCase && showValueCheckbox.checked === savedShowValue && regexpCheckbox.checked === savedMode); const re = /\*(\d+)( - [\d.]+px\))$/; const bgColors = validationResults.reduce((prev, curr, pos, array) => { const backgroundColor = curr.isValid ? (!curr.reason ? COLOR_DEFAULT : COLOR_WARNING) : COLOR_ERROR; @@ -147,17 +149,20 @@ textarea#${patternInput.id} { 'status': !statusCheckbox.checked ? 'disabled' : '', 'keywords': patternInput.value, 'mode': regexpCheckbox.checked ? 'regexp' : 'text', - 'matchCase': caseCheckbox.checked + 'matchCase': caseCheckbox.checked, + 'showValue': showValueCheckbox.checked, }); patternInput.focus(); savedKeywords = patternInput.value; savedMode = regexpCheckbox.checked; savedMatchCase = caseCheckbox.checked; + savedShowValue = showValueCheckbox.checked; e.target.disabled = true; }); statusCheckbox.addEventListener('change', async (e) => { caseCheckbox.disabled = + showValueCheckbox.disabled = regexpCheckbox.disabled = patternInput.disabled = !e.target.checked; applyButton.disabled = !e.target.checked || !validationResults.every(r => r.isValid); @@ -186,6 +191,11 @@ textarea#${patternInput.id} { patternInput.focus(); }); + showValueCheckbox.addEventListener('change', async (e) => { + await renderBackground(); + patternInput.focus(); + }); + patternInput.addEventListener('scroll', renderBackground); patternInput.addEventListener('scroll', () => { if (patternInput.scrollLeft < textAreaPaddingLeft) patternInput.scrollLeft = 0; @@ -215,11 +225,14 @@ textarea#${patternInput.id} { statusCheckbox.checked = status !== 'disabled'; savedMatchCase = caseCheckbox.checked = matchCase; + savedShowValue = showValueCheckbox.checked = showValue; + console.log(savedShowValue, savedShowValue.checked, showValue) savedMode = regexpCheckbox.checked = mode === 'regexp'; savedKeywords = patternInput.value = keywords || ''; caseCheckbox.disabled = + showValueCheckbox.disabled = regexpCheckbox.disabled = patternInput.disabled = applyButton.disabled = !statusCheckbox.checked; diff --git a/popup/main.html b/popup/main.html index e9d9ad8..9cfeb54 100644 --- a/popup/main.html +++ b/popup/main.html @@ -21,6 +21,10 @@
+