-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcontent.js
More file actions
103 lines (89 loc) · 3.4 KB
/
Copy pathcontent.js
File metadata and controls
103 lines (89 loc) · 3.4 KB
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
console.log('Highlighter loaded');
// Load saved highlights when page loads
loadHighlights();
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'highlight') {
highlightSelection(request.category, request.color);
}
});
async function loadHighlights() {
const url = window.location.href;
const result = await chrome.storage.local.get([url]);
if (result[url]) {
result[url].forEach(highlight => {
restoreHighlight(highlight.text, highlight.category, highlight.color);
});
}
}
async function saveHighlight(text, category, color) {
const url = new URL(window.location.href).origin + new URL(window.location.href).pathname;
const result = await chrome.storage.local.get([url]);
const highlights = result[url] || [];
if (highlights.length < 100) {
highlights.push({ text, category, color, timestamp: Date.now() });
await chrome.storage.local.set({ [url]: highlights });
}
}
function restoreHighlight(text, category, color) {
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
let node;
while (node = walker.nextNode()) {
if (node.textContent.includes(text) && !node.parentElement.style.backgroundColor) {
const content = node.textContent;
const index = content.indexOf(text);
if (index !== -1) {
const before = content.substring(0, index);
const highlighted = content.substring(index, index + text.length);
const after = content.substring(index + text.length);
const span = document.createElement('span');
span.style.backgroundColor = color + '80';
span.style.cursor = 'pointer';
span.title = 'Double-click to remove';
span.textContent = highlighted;
span.addEventListener('dblclick', async () => {
await removeHighlight(text);
span.outerHTML = span.textContent;
});
const parent = node.parentNode;
if (before) parent.insertBefore(document.createTextNode(before), node);
parent.insertBefore(span, node);
if (after) parent.insertBefore(document.createTextNode(after), node);
parent.removeChild(node);
break;
}
}
}
}
async function removeHighlight(text) {
const url = window.location.href;
const result = await chrome.storage.local.get([url]);
const highlights = result[url] || [];
const filtered = highlights.filter(h => h.text !== text);
await chrome.storage.local.set({ [url]: filtered });
}
function highlightSelection(category, color) {
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
let text = selection.toString().trim();
if (!text) return;
// Security: sanitize only for storage, keep original for highlighting
const sanitizedText = text.replace(/[<>"'&]/g, '').substring(0, 500);
const span = document.createElement('span');
span.style.backgroundColor = color + '80';
span.style.cursor = 'pointer';
span.title = 'Double-click to remove';
span.setAttribute('data-category', category);
span.addEventListener('dblclick', async () => {
await removeHighlight(text);
span.outerHTML = span.textContent;
});
try {
range.surroundContents(span);
selection.removeAllRanges();
saveHighlight(sanitizedText, category, color);
console.log('Highlighted and saved:', text);
} catch (e) {
console.log('Highlight failed:', e);
}
}