Skip to content

Commit 2133b60

Browse files
committed
Version 0.1.0
1 parent 2bcc3a9 commit 2133b60

File tree

12 files changed

+2425
-1
lines changed

12 files changed

+2425
-1
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/.idea/
2+
/dist/
3+
/node_modules/

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
# open-blur
1+
# OpenBlur Chrome extension
2+
3+
Automatically hide and blur sensitive information on any webpage, including emails and names.

package-lock.json

Lines changed: 2128 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "openblur",
3+
"version": "0.1.0",
4+
"repository": "https://github.com/getvictor/openblur",
5+
"author": "getvictor",
6+
"license": "MIT",
7+
"scripts": {
8+
"build": "webpack --mode production"
9+
},
10+
"devDependencies": {
11+
"@tsconfig/recommended": "^1.0.6",
12+
"@types/chrome": "^0.0.266",
13+
"copy-webpack-plugin": "^12.0.2",
14+
"ts-loader": "^9.5.1",
15+
"ts-node": "^10.9.2",
16+
"typescript": "^5.4.5",
17+
"webpack": "^5.91.0",
18+
"webpack-cli": "^5.1.4"
19+
}
20+
}

src/background.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { MODES } from "./constants"
2+
3+
let currentModeIndex = 1
4+
5+
chrome.storage.sync.get("mode", (data) => {
6+
if (data.mode) {
7+
currentModeIndex = data.mode.index
8+
}
9+
chrome.action.setBadgeText({text: MODES[currentModeIndex].text}).then(r => {})
10+
chrome.action.setBadgeBackgroundColor({color: MODES[currentModeIndex].color}).then(r => {})
11+
})

src/constants.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const MODES = [
2+
{
3+
index: 0,
4+
id: "off",
5+
text: "off",
6+
color: "#AAAAAA",
7+
},
8+
{
9+
index: 1,
10+
id: "on",
11+
text: "blur",
12+
color: "#008C20",
13+
},
14+
]
15+
export const NUMBER_OF_ITEMS = 10

src/content.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { NUMBER_OF_ITEMS} from "./constants"
2+
3+
const contentToBlur: string[] = []
4+
const blurFilter = "blur(6px)"
5+
6+
function processNode(node: Node) {
7+
if (node.childNodes.length > 0) {
8+
Array.from(node.childNodes).forEach(processNode)
9+
}
10+
if (node.nodeType === Node.TEXT_NODE && node.textContent !== null && node.textContent.trim().length > 0) {
11+
const parent = node.parentElement
12+
if (parent !== null && (parent.tagName === 'SCRIPT' || parent.style.filter === blurFilter)) {
13+
// Already blurred
14+
return
15+
}
16+
const text = node.textContent!
17+
contentToBlur.some((content) => {
18+
if (text.includes(content)) {
19+
blurElement(parent!)
20+
return true
21+
}
22+
return false
23+
})
24+
25+
}
26+
}
27+
28+
function blurElement(elem: HTMLElement) {
29+
elem.style.filter = blurFilter
30+
console.debug("blurred id:" + elem.id + " class:" + elem.className + " tag:" + elem.tagName + " text:" + elem.textContent)
31+
}
32+
33+
const observer = new MutationObserver((mutations, observer) => {
34+
mutations.forEach((mutation) => {
35+
if (mutation.addedNodes.length > 0) {
36+
mutation.addedNodes.forEach(processNode)
37+
} else {
38+
processNode(mutation.target)
39+
}
40+
})
41+
})
42+
43+
let enabled = true
44+
const keys = ["mode"]
45+
for (let i = 0; i < NUMBER_OF_ITEMS; i++) {
46+
keys.push(`item_${i}`)
47+
}
48+
49+
chrome.storage.sync.get(keys, (data) => {
50+
if (data.mode && data.mode.id === "off") {
51+
enabled = false
52+
}
53+
for (let i = 0; i < NUMBER_OF_ITEMS; i++) {
54+
const item = data[`item_${i}`]
55+
if (item) {
56+
contentToBlur.push(item)
57+
}
58+
}
59+
if (enabled) {
60+
observer.observe(document, {
61+
attributes: false,
62+
characterData: true,
63+
childList: true,
64+
subtree: true,
65+
})
66+
67+
// Loop through all elements on the page.
68+
processNode(document)
69+
}
70+
})

src/manifest.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "OpenBlur",
4+
"version": "0.1.0",
5+
"description": "Automatically hide and blur sensitive information on any webpage, including emails and names.",
6+
"action": {
7+
"default_popup": "popup.html"
8+
},
9+
"permissions": [
10+
"storage"
11+
],
12+
"background": {
13+
"service_worker": "background.js"
14+
},
15+
"content_scripts": [
16+
{
17+
"matches": ["<all_urls>"],
18+
"js": ["content.js"]
19+
}
20+
]
21+
}

src/popup.html

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<html lang="en">
2+
<head>
3+
<title>Water Popup</title>
4+
<style>
5+
/* The switch - the box around the slider */
6+
.switch {
7+
position: relative;
8+
display: inline-block;
9+
width: 60px;
10+
height: 34px;
11+
}
12+
13+
/* Hide default HTML checkbox */
14+
.switch input {
15+
opacity: 0;
16+
width: 0;
17+
height: 0;
18+
}
19+
20+
/* The slider */
21+
.slider {
22+
position: absolute;
23+
cursor: pointer;
24+
top: 0;
25+
left: 0;
26+
right: 0;
27+
bottom: 0;
28+
background-color: #ccc;
29+
/*transition: .4s;*/ /* TODO: Bring back animation but disable on initial load. */
30+
}
31+
32+
.slider:before {
33+
position: absolute;
34+
content: "";
35+
height: 26px;
36+
width: 26px;
37+
left: 4px;
38+
bottom: 4px;
39+
background-color: white;
40+
/*transition: .4s;*/
41+
}
42+
43+
input:checked + .slider {
44+
background-color: #2196F3;
45+
}
46+
47+
input:focus + .slider {
48+
box-shadow: 0 0 1px #2196F3;
49+
}
50+
51+
input:checked + .slider:before {
52+
transform: translateX(26px);
53+
}
54+
55+
/* Rounded sliders */
56+
.slider.round {
57+
border-radius: 34px;
58+
}
59+
60+
.slider.round:before {
61+
border-radius: 50%;
62+
}
63+
64+
.secret {
65+
margin: 5px;
66+
}
67+
</style>
68+
</head>
69+
<body>
70+
<label class="switch">
71+
<input type="checkbox" id="enabled">
72+
<span class="slider round"></span>
73+
</label>
74+
<input class="secret" type="password" id="item_0">
75+
<input class="secret" type="password" id="item_1">
76+
<input class="secret" type="password" id="item_2">
77+
<input class="secret" type="password" id="item_3">
78+
<input class="secret" type="password" id="item_4">
79+
<input class="secret" type="password" id="item_5">
80+
<input class="secret" type="password" id="item_6">
81+
<input class="secret" type="password" id="item_7">
82+
<input class="secret" type="password" id="item_8">
83+
<input class="secret" type="password" id="item_9">
84+
<script src="popup.js"></script>
85+
</body>
86+
</html>

src/popup.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { MODES, NUMBER_OF_ITEMS } from "./constants"
2+
3+
console.log("Hello, world from popup!")
4+
5+
const checkbox = document.getElementById("enabled") as HTMLInputElement
6+
chrome.storage.sync.get("mode", (data) => {
7+
checkbox.checked = !(data.mode && data.mode.id === "off");
8+
const mode = data.mode || MODES[1]
9+
chrome.action.setBadgeText({text: mode.text}).then(r => {})
10+
chrome.action.setBadgeBackgroundColor({color: mode.color}).then(r => {})
11+
})
12+
checkbox.addEventListener("change", (event) => {
13+
if (event.target instanceof HTMLInputElement) {
14+
const mode = event.target.checked ? MODES[1] : MODES[0]
15+
chrome.storage.sync.set({mode}, () => {})
16+
chrome.action.setBadgeText({text: mode.text}).then(r => {})
17+
chrome.action.setBadgeBackgroundColor({color: mode.color}).then(r => {})
18+
}
19+
})
20+
21+
// Loop over NUMBER_OF_ITEMS elements and listen to each one.
22+
for (let i = 0; i < NUMBER_OF_ITEMS; i++) {
23+
const input = document.getElementById(`item_${i}`) as HTMLInputElement
24+
// TODO: optimize to get all stored items at once
25+
chrome.storage.sync.get(`item_${i}`, (data) => {
26+
input.value = data[`item_${i}`] || ""
27+
})
28+
input.addEventListener("change", (event) => {
29+
if (event.target instanceof HTMLInputElement) {
30+
chrome.storage.sync.set({[`item_${i}`]: event.target.value}, () => {})
31+
}
32+
})
33+
}

0 commit comments

Comments
 (0)