-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcontent.ts
106 lines (95 loc) · 2.71 KB
/
content.ts
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
import { Storage } from "@plasmohq/storage"
import {
EVENT_CLEAR_TRANSLATE,
EVENT_TRANSLATE,
HOT_KEYS,
LANGUAGE,
TRANSLATED_KEY
} from "~app/constant"
import { isMatchHotKey, isValidHotKey } from "~app/hot_keys"
import { injectAfter, removeInjectedElements } from "~app/injector"
window.addEventListener("keydown", async (e) => {
const storage = new Storage()
const hotKey = await storage.get(HOT_KEYS)
const language = await storage.get(LANGUAGE)
if (isValidHotKey(hotKey) && isMatchHotKey(hotKey, e)) {
translatePage(language)
}
})
chrome.runtime.onMessage.addListener(({ event, language }) => {
switch (event) {
case EVENT_TRANSLATE:
translatePage(language)
break
case EVENT_CLEAR_TRANSLATE:
clearTranslations()
break
}
})
function clearTranslations() {
const elements = document.body.parentNode?.querySelectorAll(
`[data-${TRANSLATED_KEY}="true"]`
)
elements?.forEach((element: HTMLElement) => {
delete element.dataset[TRANSLATED_KEY]
})
removeInjectedElements(document.body)
}
function translatePage(language: string) {
clearTranslations()
const textNodes = getTextNodes(document.body)
const nodes = new Set<HTMLElement>()
textNodes.forEach((it) => {
const translateElement = getTranslatableElement(it.parentElement)
if (!translateElement || translateElement.dataset[TRANSLATED_KEY]) {
return
}
translateElement.dataset[TRANSLATED_KEY] = "true"
getLeafNodes([translateElement]).forEach((it) => {
nodes.add(it)
})
})
nodes.forEach((it) => {
chrome.runtime.sendMessage({ text: it.innerText, language }, (resp) => {
const injectedNode = injectAfter(it, "p", resp.text)
injectedNode.dataset[TRANSLATED_KEY] = "true"
})
})
}
function getTextNodes(el: Node) {
var n: Node,
a: Node[] = [],
walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null)
while ((n = walk.nextNode())) {
if (n.textContent?.trim() !== "") {
a.push(n)
}
}
return a
}
function getTranslatableElement(el: HTMLElement): HTMLElement | null {
const tags = ["h1", "h2", "h3", "h4", "h5", "h6", "p", "li"]
const element = tags.reduce<HTMLElement | null>((acc, tag) => {
if (acc) {
return acc
}
return el.closest(tag)
}, null)
return element
}
function getLeafNodes(
nodes: HTMLElement[] | NodeListOf<ChildNode>,
result = []
): HTMLElement[] {
for (var i = 0, length = nodes.length; i < length; i++) {
const isLeaf = [...nodes[i].childNodes].some(
(it) => it.nodeType === Node.TEXT_NODE && (it as Text).data.trim() !== ""
)
if (isLeaf) {
result.push(nodes[i])
} else {
result = getLeafNodes(nodes[i].childNodes, result)
}
}
return result
}