-
Notifications
You must be signed in to change notification settings - Fork 226
/
AC-Translate-Youdao.user.js
368 lines (356 loc) · 13.8 KB
/
AC-Translate-Youdao.user.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
// ==UserScript==
// @name AC-必应取词+翻译
// @description 一个可以在浏览器中自由使用的屏幕取词脚本-alt+鼠标翻译,或者选中按Q翻译;必应智能翻译,翻译精准
// @version 2.6
// @namespace youdao
// @license GPL-3.0-only
// @author AC -modified from :Liu Yuyang(sa@linuxer.me)
// @include *
// @icon https://gitee.com/remixAC/GM_script/raw/master/images/head.jpg
// @require https://greasyfork.org/scripts/10783-md5/code/md5.js?version=60242
// @run-at document-end
// @note V2.6 2020-07-19 修复翻译API失效的问题
// @note V2.5 2019-12-05 修复由于之前没注意的点导致的部分网站运行异常的问题
// @note V2.3 2019-09-06 搜狗翻译彻底失效,由于搜狗采用的方案越来越狗血,无法较好的获取到实际的sign,因此放弃使用搜狗,转为使用必应来进行翻译
// @note V2.2 2019-08-28 更新搜狗翻译失效的问题,修改为搜狗的api翻译
// @note V2.1 2019-05-25 继续修复翻译失效的问题,后来发现是Resouece无法即时的刷新,所以现在采用脚本自动的获取数据,同时也支持了其他脚本的运行
// @note V2.0 2019-03-24 只支持Tampermonkey。由于GM_getResourceText在GM下的不支持的原因,所以暂时只支持Tampermonkey的运行
// @note V1.9 2019-02-21 继续更新搜狗API更新导致的无法翻译的bug -- 变更为库文件更新模式,可以很方便的更新token
// @note V1.8 2018-12-19 更新搜狗API的key,且修复了部分特殊关键词无法翻译的bug
// @note V1.7 2018-12-04 修复搜狗搜索API更新的问题--已经更换为搜狗的翻译API;如果搜狗更新的话,那估计又要换回必应翻译了
// @note V1.6 2018-05-23 修复部分英文单词翻译不出来的bug,同时修复特殊的BOLD标签
// @note V1.5 2018-05-16 修复英文翻译单词的bug,同时修复语音的问题,暂时就这样了,最近不更新了,有需要的话提出来,以后再说
// @note V1.4 2018-05-16 修复上一次更新导致的bug,同时修复多种语言翻译,结果却翻译成了英文的bug
// @note V1.3 2018-05-16 修复word词组的时候翻译出错的问题
// @note V1.2 2018-05-16 修复没有添加connect的错误问题
// @note V1.1 2018-05-12 有道自己不更新~~HTTPS证书过期,现在使用搜狗翻译吧,老司机们说搜狗翻译还是很有优势的,至于百度翻译,再说吧
// @note V1.0 2017-11-22 修复firefox57的翻译的BUG
// @note V0.9 2017-11-15 修复了一大堆,优化了下界面的展示问题
// @note V0.8 2017-2-21 修正了由于z-index过低导致的淘宝页面翻译过于底层而看不见
// @note V0.7 2016-9-29 修正了部分格式错误,换行的时候稍微好了点
// @note V0.6 2016-1-12 终于修正了,之前只改了句子翻译中的文字颜色,才发现单词翻译后的颜色简直看不清。。。
// @note V0.5 2016-1-9 修改默认背景色
// @note V0.4 2016-1-8 修正chrome的支持
// @note V0.3 2016-1-8 修改窗体默认大小,尽可能的显示回车功能,避免了以前的全部显示在同一行--依旧有bug
// @note V0.2 2015-11-26 修改部分地方适合自己使用,选中文字,按下Q翻译,或者alt+鼠标选中,自动翻译
// @grant GM_xmlhttpRequest
// @connect fanyi.youdao.com
// @connect fanyi.sogou.com
// @connect xbaidu.ntaow.com
// @connect cn.bing.com
// @connect www.bing.com
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM.getValue
// @grant GM_setValue
// @grant GM.setValue
// ==/UserScript==
(function(){
var curX;
var curY;
var timeout;
var transResult;
var Config = {}; // 之前忘了加上参数的定义,导致了污染全局的变量
if (typeof(GM) == "undefined") {
// 这个是ViolentMonkey的支持选项
GM = {};
GM.setValue = GM_setValue;
GM.getValue = GM_getValue;
}
Promise.all([GM.getValue("Config")]).then(function (data) {
if (data[0] != null) {
Config = data[0];
}else{
Config = {};
}
// 初始化完成之后才能调用正常函数
callback();
}).catch(function (except) {
console.log(except);
});
function callback() {
document.body.addEventListener("mousemove", function(e){curX = e.clientX; curY = e.clientY;}, false);
document.body.addEventListener("keypress", callbackWrapper, false);
document.body.addEventListener("mouseup", callbackWrapper, false);
document.body.addEventListener("dblclick", callbackWrapper, false);
GM_addStyle("bold{font-weight: bold;}");
}
function callbackWrapper(e) {
if(e.type === 'dblclick' && isInACTransPop()){
// console.log("双击操作:复制目标文本");
// console.log(transResult);
return;
}
if(e.charCode == 113 || e.type == "mouseup"){ //Q
// remove previous .ACyoudaoPopup if exists
var previous = document.querySelector(".ACyoudaoPopup");
if(previous && isInACTransPop()){
document.body.removeChild(previous);
transResult = "";
}
// quick fix. with ctrl key held
if (e.type == "mouseup" && !e.altKey) {
return;
}
clearTimeout(timeout);
timeout = setTimeout(function(){
translate();
}, 120);
}
}
function isInACTransPop(){
var checkNode = document.getSelection().anchorNode.parentNode;
return checkNode && (""+checkNode.className).indexOf("ACyoudao") < 0;
}
function translate() {
var selectObj = document.getSelection();
if (selectObj.anchorNode.nodeType == 3) {
var word = selectObj.toString();
if (word == "") {
return;
}
word = word.replace(new RegExp('(\r\n)+', 'g'), 'QWER');// 特殊标记便于替换
var ts = new Date().getTime();
var mx = curX;
var my = curY;
translate(word, ts);
}
function popup(mx, my, result) {
var youdaoWindow = document.createElement('div');
youdaoWindow.classList.toggle("ACyoudaoPopup");
// parse
var res = JSON.parse(result);
var dictJSON = res[0]['translations'][0];
var query = dictJSON['displayTarget'] || dictJSON['text'];
// var errorCode = dictJSON['errorCode'];
if (dictJSON['dictionary']) {
word();
} else {
sentence();
}
// main window
// first insert into dom then there is offsetHeight!IMPORTANT!
document.body.appendChild(youdaoWindow);
youdaoWindow.style = "color: #000; text-align: left; position: fixed; background: rgb(237, 246, 255); border-radius: 3px; box-shadow: 0px 0px 5px 0px; opacity: 1; min-width: 300px; max-width: 550px; overflow-wrap: break-word; font-size: 15px;padding: 3px; z-index: 2147483647;display:block !important;";
youdaoWindow.style.left = mx + 10 + "px";
if (mx + 200 + 30 >= window.innerWidth) {
youdaoWindow.style.left = parseInt(youdaoWindow.style.left) - 200 + "px";
}
//alert(window.innerHeight);
if (my + youdaoWindow.offsetHeight + 30 >= window.innerHeight) {
youdaoWindow.style.bottom = "20px";
} else {
youdaoWindow.style.top = my + "px";
}
function word() {
function play(soundURL) {
if(soundURL.indexOf("http") != 0) soundURL = "https:" + soundURL;
function playSound(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}
var context = new AudioContext();
// var soundUrl = 'https://dict.youdao.com/dictvoice?type=2&audio='+encodeURIComponent(word);
console.log(soundURL);
var soundUrl = soundURL;
var p = new Promise(function(resolve, reject) {
var ret = GM_xmlhttpRequest({
method: "GET",
url: soundUrl,
responseType: 'arraybuffer',
onload: function(res) {
try {
context.decodeAudioData(res.response, function(buffer) {
resolve(buffer);
});
} catch(e) {
reject(e);
}
}
});
});
p.then(playSound, function(e) {
console.log(e);
});
}
// var basic = dictJSON['basic'] != null ? dictJSON['basic'] : dictJSON['dictionary']['content'][0];
var basic = dictJSON;
var header = document.createElement('div');
var phoneticText = [];
header.className = "ACyoudaoPopupDIV";
// header
var span = document.createElement('span');
span.className = "ACyoudaoPopupSpan";
span.innerHTML = query;
header.appendChild(span);
// phonetic if there is
var phonetic = basic['phonetic'];
var transWord;
try{
transWord = basic['category'][0]['sense'][0]['description'];
}catch (e) {
transWord = basic['usual'][0]['values'];
}
if(typeof(phonetic) == "object"){
var i = 0;
phonetic.map(function(one){
phoneticText[i] = one['text']+";";
i++;
});
}else{
phoneticText[0] = phonetic;
}
for(var i = 0; i < phoneticText.length; i++){
var phoneticNode = document.createElement('span');
if(i == phoneticText.length-1)
phoneticNode.innerHTML = '[' + phoneticText[i] + ']:'+transWord;
else
phoneticNode.innerHTML = '[' + phoneticText[i] + ']';
phoneticNode.style.cursor = "pointer";
header.appendChild(phoneticNode);
var playLogo = document.createElement('span');
header.appendChild(phoneticNode);
(function(index){
phoneticNode.addEventListener('mouseup', function(e){
if (e.target === phoneticNode) { }
e.stopPropagation();
play(phonetic[index]['filename']);
}, false);
})(i);
}
header.style.lineHeight = "1.8";
header.style.color = "black";
header.style.margin = "0";
header.style.padding = "0";
span.style.fontSize = "15px";
span.style.color = "black";
youdaoWindow.appendChild(header);
var hr = document.createElement('hr');
hr.className = "ACyoudaoPopupHR";
hr.style.margin = "0";
hr.style.padding = "0";
hr.style.height = "1px";
hr.style.borderTop = "dashed 1px black";
youdaoWindow.appendChild(hr);
var ul = document.createElement('ul');
ul.className = "ACyoudaoPopupUL";
// ul style
ul.style.margin = "0";
ul.style.padding = "0";
var mapNode;
try{
mapNode = basic['category'][0]['sense'][0]['example'];
}catch (e) {
mapNode = basic['content'][0]['item']['core'];
}
mapNode.map(function(trans) {
var li = document.createElement('li');
li.className = "ACyoudaoPopupLi";
li.style.listStyle = "none";
li.style.margin = "0";
li.style.padding = "0";
li.style.background = "none";
li.style.color = "#008CD8";
var innerHTML = "";
if(trans['source'] != undefined){
innerHTML = trans['source']+":"+trans['target'];
}else{
innerHTML = trans['detail']['zh']+":" + trans['detail']['en']+":";
}
var divNode = document.createElement("div");
divNode.innerHTML = innerHTML;
li.appendChild(divNode);
ul.appendChild(li);
});
youdaoWindow.appendChild(ul);
}
function sentence() {
var ul = document.createElement('ul');
ul.className = "ACyoudaoPopupUL";
// ul style
ul.style.margin = "0";
ul.style.padding = "0";
var trans = dictJSON['displayTarget'] || dictJSON['text']; // 翻译结果
// var oriText = dictJSON['translate']['text']; // 原始文本-找不到
var li = document.createElement('li');
li.style.listStyle = "none";
li.style.margin = "0";
li.style.padding = "0";
li.style.background = "none";
li.style.color = "#008CD8";
li.className = "ACyoudaoPopupLi";
trans = trans.replaceAll('QWER', '\n').replaceAll(",", ", ");
console.log(trans);
// if(oriText.length < 70 && !/\n/.test(oriText)) // 如果长度比较短,并且没有回车符
// trans = oriText+"\n"+trans;
transResult += trans;
var reg = new RegExp("\n","gm");
while(reg.exec(trans) != null){// 如果找不到回车了,那么最后重复一次新建<p>
//找到一个回车,那么添加一个新的<p>,内容为:trans.sub(0, ?); trans = trans.sub(?);
var newNode = document.createElement("li");
newNode.className = "ACyoudaoPopupLi";
newNode.style.listStyle = "none";
newNode.style.margin = "0";
newNode.style.padding = "0";
newNode.style.background = "none";
newNode.style.color = "#008CD8";
newNode.style.fontSize = "15px";
newNode.innerHTML = trans.substring(0, reg.lastIndex-1);
li.appendChild(newNode);
trans = trans.substring(reg.lastIndex);
}
var newNode = document.createElement("div");
newNode.className = "ACyoudaoPopupDiv";
newNode.innerHTML = trans;
newNode.style = "line-height: 1.8;color:#008CD8 !important;font-size:15px !important;margin-left:10px;margin-right:10px;";
li.appendChild(newNode);
ul.appendChild(li);
youdaoWindow.appendChild(ul);
}
}
function ToCDB(str) {
var tmp = "";
for(var i=0;i<str.length;i++)
{
if(str.charCodeAt(i)>65248&&str.charCodeAt(i)<65375)
{
tmp += String.fromCharCode(str.charCodeAt(i)-65248);
}
else
{
tmp += String.fromCharCode(str.charCodeAt(i));
}
}
return tmp
}
function translate(word, ts) {
var ToLANGUAGE = "en";
if(!/[\u4E00-\u9FA5]/g.test(word)) // 如果没有中文,那么说明是外文--> to=中文
ToLANGUAGE = "zh-Hans";
var data = `fromLang=auto-detect&to=${ToLANGUAGE}&text=${encodeURIComponent(word)}`;
console.log(data);
var ret = GM_xmlhttpRequest({
url: "https://www.bing.com/ttranslatev3",
method: "POST",
data: data,
binary: true,
headers: {"Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded"}, // can be omitted...
onload: function(res) {
var retContent = res.response;
console.log(retContent);
retContent = ToCDB(retContent);
//console.log(retContent)
popup(mx, my, retContent);
},
onerror: function(res) {
console.log("error");
}
});
}
String.prototype.replaceAll = function(s1,s2){
return this.replace(new RegExp(s1,"gm"),s2);
};
}
})();