-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.js
219 lines (181 loc) · 5.93 KB
/
content.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Add at the top of content.js
const CLASSNAME = 'ease-word-highlighter-span';
let intervalId = null;
// Modify the main execution block
// content.js
(async function() {
try {
const {config} = await chrome.storage.local.get('config');
if (!config?.wordHighlights) return;
if (config?.domains?.length &&
!config.domains.includes(window.location.hostname)) {
return;
}
let intervalId = null;
const runHighlights = () => {
document.querySelectorAll(CLASSNAME).forEach(el => el.remove());
const sortedWords = [...config.wordHighlights].sort((a, b) =>
b.word.length - a.word.length
);
highlightWords({...config, wordHighlights: sortedWords});
};
// Initial runs with delays
const delays = config.rescanDelays || [0];
delays.forEach((delay, index) => {
setTimeout(() => {
runHighlights();
// Start periodic after last delay
if (index === delays.length - 1 && config.rescanInterval) {
intervalId = setInterval(runHighlights, config.rescanInterval);
}
}, delay);
});
// Cleanup on page unload
window.addEventListener('unload', () => {
clearInterval(intervalId);
});
} catch (error) {
console.error('Error:', error);
}
})();
// Add class to highlight spans in highlightWords function
// Modify the span creation line to:
// (async function() {
// try {
// const {config} = await chrome.storage.local.get('config');
// if (!config) return;
// // Keep original processing code below
// const sortedWordHighlights = [...config.wordHighlights].sort((a, b) => b.word.length - a.word.length);
// let fixedConfig = {...config}
// fixedConfig.wordHighlights = sortedWordHighlights
// highlightWords(fixedConfig);
// } catch (error) {
// console.error('Error loading config:', error);
// }
// })();
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlightWords(config) {
// Existing highlighting logic with emoji support
// Modify the config iteration line to:
config.wordHighlights.forEach(({ word, color, emoji, position }) => {
let pattern;
if (emoji) {
const escapedWord = escapeRegExp(word);
const escapedEmoji = escapeRegExp(emoji);
switch(position) {
case 'start':
pattern = `(?<!${escapedEmoji})${escapedWord}`;
break;
case 'end':
pattern = `${escapedWord}(?!${escapedEmoji})`;
break;
default: // replace
pattern = escapedWord;
}
} else {
pattern = escapeRegExp(word);
}
const regex = new RegExp(pattern, 'g');
// Find all text nodes in the document
const treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
{
acceptNode: function(node) {
// Skip certain elements
const tagName = node.parentNode.tagName.toLowerCase();
return ['script', 'style', 'textarea', 'noscript'].includes(tagName)
? NodeFilter.FILTER_SKIP
: NodeFilter.FILTER_ACCEPT;
}
}
);
const textNodes = [];
while (treeWalker.nextNode()) textNodes.push(treeWalker.currentNode);
textNodes.forEach(textNode => {
const content = textNode.nodeValue;
if (!regex.test(content)) return;
const parent = textNode.parentNode;
const newNodes = [];
let lastIndex = 0;
content.replace(regex, (match, offset) => {
// Add preceding text
if (offset > lastIndex) {
newNodes.push(
document.createTextNode(content.slice(lastIndex, offset))
);
}
// Create highlight span
const span = document.createElement('span');
// span.style.backgroundColor = color;
// span.textContent = match;
span.style.backgroundColor = color;
if (color && color.toLowerCase() !== 'no') {
span.style.backgroundColor = color;
}
// With:
if (emoji) {
span.textContent = {
replace: emoji,
start: emoji + match,
end: match + emoji,
no: match
}[position || 'no'];
} else {
span.textContent = match;
}
newNodes.push(span);
lastIndex = offset + match.length;
return match;
});
// Add remaining text
if (lastIndex < content.length) {
newNodes.push(document.createTextNode(content.slice(lastIndex)));
}
// Replace original text node
newNodes.forEach(node => parent.insertBefore(node, textNode));
parent.removeChild(textNode);
});
});
// Semantic highlighting
// Add semantic highlighting if enabled
try {
if (config.semanticHighlighting?.enabled) {
applySemanticHighlighting(config);
}
} catch (error) {
console.error('Error loading config:', error);
}
}
// Helper functions
function evaluateXPath(xpath) {
const results = [];
const snapshot = document.evaluate(xpath, document, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < snapshot.snapshotLength; i++) {
results.push(snapshot.snapshotItem(i));
}
return results;
}
function getColorForWord(word, palette) {
const hash = Array.from(word).reduce((acc, char) =>
acc + char.charCodeAt(0), 0);
return palette[hash % palette.length];
}
function applySemanticHighlighting(config) {
const palette = config.semanticHighlighting.colorPalette;
const nodes = evaluateXPath(config.semanticHighlighting.selector);
nodes.forEach(node => {
const text = node.textContent;
const words = text.match(/\b\w+\b/g) || [];
words.forEach(word => {
const color = getColorForWord(word, palette);
node.innerHTML = node.innerHTML.replace(
new RegExp(escapeRegExp(word), 'g'),
`<span style="background:${color}">${word}</span>`
);
});
});
}