-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.js
183 lines (183 loc) · 5.37 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
let isSelecting = false; // 是否正在选择文本
let transVisible = false;
let line
let content
let dic = new Map()
let user = {}
let isOpen = true // 扩展是否启用
chrome.storage.local.get('user', (res) => {
user = res.user
})
chrome.storage.local.get('isOpen', (res) => {
isOpen = res.isOpen
})
chrome.storage.onChanged.addListener((changes, namespace) => {
if (changes.user) user = changes.user.newValue
if (changes.isOpen) isOpen = changes.isOpen.newValue
});
const getNodes = (root) => {
const nodes = []
const excludeNodes = ['SCRIPT', 'NOSCRIPT', 'path', 'IMG', 'svg', 'I', 'STYLE', 'CODE', 'S'];
const doNext = (next) => {
if (!next) return;
for (let i = 0; i < next.children.length; i++) {
var node = next.children[i];
if (node && !excludeNodes.includes(node.nodeName)) {
nodes.push(node);
if (node.children) doNext(node);
}
}
return nodes
}
return doNext(root)
}
const replaceHtml = (root) => {
const nodes = getNodes(root)
if (!nodes || !nodes[0]) return
nodes.forEach((item) => {
item.childNodes.forEach((subItem) => {
const textContent = subItem.textContent;
if (subItem.nodeType === 3 && textContent.trim() !== '' && !textContent.includes('/')) {
const newNode = document.createElement('s')
newNode.style.all = 'unset';
const html = textContent.replace(/[a-zA-Z]+/g, (m) => `<s class="less-translate" style="all: unset;">${m}</s>`)
newNode.innerHTML = html
item.insertBefore(newNode, subItem)
item.removeChild(subItem)
}
})
})
}
const createTrans = () => {
line = document.createElement('s');
line.id = 'et-line';
document.body.appendChild(line)
content = document.createElement('s')
content.id = 'et-content';
content.addEventListener('mousedown', (e) => {
e.stopPropagation()
})
content.addEventListener('mouseup', (e) => {
e.stopPropagation()
})
content.addEventListener('dblclick', (e) => {
e.stopPropagation()
})
document.body.appendChild(content)
}
const updateTrans = ({ target, translation, type }) => {
try {
if (!content) createTrans();
content = document.getElementById('et-content');
line = document.getElementById('et-line');
const targetRect = target.getBoundingClientRect();
if (type === 'hover') line.style.cssText = `display: block; top: ${targetRect.bottom}px; left: ${targetRect.left}px; width: ${target.offsetWidth}px;`;
content.innerText = translation;
content.style.cssText = `display: block; left: ${targetRect.left}px;`;
const dis = type === 'hover' ? 4 : 12
content.style.top = `${targetRect.top - content.offsetHeight - dis}px`;
if (content.getBoundingClientRect().top < 0) {
content.style.top = `${targetRect.bottom + 10}px`
}
transVisible = true
} catch (err) {}
}
const initParams = (q) => {
const appid = user.appId
const salt = Math.random()
const secretKey = user.secretKey
return {
q,
from: 'auto',
to: 'zh',
appid,
salt,
sign: MD5(`${appid}${q}${salt}${secretKey}`)
}
}
const getTranslation = (q) => {
return new Promise((resolve, reject) => {
if (!user) {
resolve('请先点击 Less Translate 扩展图标进行配置')
return
}
if (dic.has(q)) {
resolve(dic.get(q))
} else {
try {
chrome.runtime.sendMessage({
type: 'getTranslation',
data: initParams(q)
}, (res) => {
const dst = res.trans_result?.reduce((acc,curr) => `${acc}${curr.dst}\n`, '')
if (dst) {
if (!q.includes(' ')) dic.set(q, dst); // 不缓存段落
resolve(dst)
} else {
reject()
}
});
} catch (err) {}
}
})
}
const hideTrans = () => {
if (line) line.style.display = 'none';
if (content) content.style.display = 'none';
transVisible = false;
}
// hover时提示
document.addEventListener('mouseover', debounce((e) => {
if (!isOpen || isSelecting) return
const target = e.target
replaceHtml(target.parentNode)
if (target.className == 'less-translate') {
let isHovering = true
const hideFn = () => {
isHovering = false
if (line) {
hideTrans()
}
target.removeEventListener('mouseout', hideFn)
}
target.addEventListener('mouseout', hideFn)
getTranslation(target.innerText).then((v) => {
if (isHovering) updateTrans({ target, translation: v, type: 'hover' })
})
}
}, 100))
let selectStartTime = 0; // 过滤掉click意外触发
document.addEventListener('mousedown', () => {
selectStartTime = Date.now()
isSelecting = true
hideTrans()
})
// 处理选中文本
const handleSelection = () => {
const selection = getSelection();
const q = selection.toString();
if (!q) return;
// 判断是否是时间戳
if (isTimestamp(q)) {
updateTrans({ target: selection.getRangeAt(0), translation: timestampToDateTimeString(q) })
} else {
getTranslation(q).then((v) => {
updateTrans({ target: selection.getRangeAt(0), translation: v })
})
}
}
// 选择文本后提示
document.addEventListener('mouseup', () => {
if (!isOpen) return
isSelecting = false
if (Date.now() - selectStartTime < 500) return
handleSelection()
})
// 双击选择文本
document.addEventListener('dblclick', (e) => {
if (!isOpen) return
handleSelection()
});
window.addEventListener('scroll', () => {
if (transVisible) hideTrans();
})