297 lines
13 KiB
JavaScript
297 lines
13 KiB
JavaScript
|
// ==UserScript==
|
|||
|
// @name Translate
|
|||
|
// @namespace http://tampermonkey.net/
|
|||
|
// @version 1.4
|
|||
|
// @description 划词翻译调用“必应翻译(必应词典)、谷歌翻译、有道词典(有道翻译)、百度翻译”网页翻译
|
|||
|
// @author barrer
|
|||
|
// @match http://*/*
|
|||
|
// @include https://*/*
|
|||
|
// @include file:///*
|
|||
|
// @run-at document-end
|
|||
|
// @connect dict.youdao.com
|
|||
|
// @connect cn.bing.com
|
|||
|
// @connect translate.googleapis.com
|
|||
|
// @connect fanyi.baidu.com
|
|||
|
// @grant GM_xmlhttpRequest
|
|||
|
// ==/UserScript==
|
|||
|
|
|||
|
(function () {
|
|||
|
'use strict';
|
|||
|
|
|||
|
// Your code here...
|
|||
|
/**日志输出*/
|
|||
|
function log() {
|
|||
|
var debug = false;
|
|||
|
if (!debug)
|
|||
|
return;
|
|||
|
if (arguments) {
|
|||
|
for (var i = 0; i < arguments.length; i++) {
|
|||
|
console.log(arguments[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
log('url:' + window.location.href);
|
|||
|
// 翻译图标
|
|||
|
var icon = document.createElement('div'), style = '' +
|
|||
|
'font-family:Arial,sans-serif!important;' +
|
|||
|
'font-weight:normal!important;' +
|
|||
|
'background:#f60!important;' +
|
|||
|
'color:#fff!important;' +
|
|||
|
'border-radius:3px!important;' +
|
|||
|
'font-size:13px!important;' +
|
|||
|
'line-height:100%!important;' +
|
|||
|
'padding:2px 4px!important;' +
|
|||
|
'margin:0 4px!important;' +
|
|||
|
'display:inline-block!important;' +
|
|||
|
'text-decoration:none!important;' +
|
|||
|
'';
|
|||
|
icon.innerHTML = '' +
|
|||
|
'<a href="javascript:void(0)" style="' + style + '" type="bing">必应</a>' +
|
|||
|
'<a href="javascript:void(0)" style="' + style + '" type="google">谷歌</a>' +
|
|||
|
'<a href="javascript:void(0)" style="' + style + '" type="youdao">有道</a>' +
|
|||
|
'<a href="javascript:void(0)" style="' + style + '" type="baidu_translator">百度</a>' +
|
|||
|
'';
|
|||
|
icon.setAttribute('style', '' +
|
|||
|
'display:none!important;' +
|
|||
|
'position:absolute!important;' +
|
|||
|
'font-size:13px!important;' +
|
|||
|
'text-align:left!important;' +
|
|||
|
'z-index:2147483647!important;' +
|
|||
|
'');
|
|||
|
// 添加翻译图标到 DOM
|
|||
|
document.documentElement.appendChild(icon);
|
|||
|
// 鼠标事件:防止选中的文本消失
|
|||
|
document.addEventListener('mousedown', function (e) {
|
|||
|
if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) {// 点击了翻译图标
|
|||
|
e.preventDefault();
|
|||
|
}
|
|||
|
});
|
|||
|
// 选中变化事件:当点击已经选中的文本的时候,隐藏翻译图标和翻译面板(此时浏览器动作是:选中的文本已经取消选中了)
|
|||
|
document.addEventListener("selectionchange", function () {
|
|||
|
log('selectionchange:' + window.getSelection().toString());
|
|||
|
if (!window.getSelection().toString().trim()) {
|
|||
|
icon.style.display = 'none';
|
|||
|
server.containerDestroy();
|
|||
|
}
|
|||
|
});
|
|||
|
// 鼠标事件:防止选中的文本消失;显示、隐藏翻译图标
|
|||
|
document.addEventListener('mouseup', function (e) {
|
|||
|
if (e.target == icon || (e.target.parentNode && e.target.parentNode == icon)) {// 点击了翻译图标
|
|||
|
e.preventDefault();
|
|||
|
return;
|
|||
|
}
|
|||
|
for (var i = 0; i < server.rendered.length; i++) {// 点击了翻译内容面板
|
|||
|
if (e.target == server.rendered[i])
|
|||
|
return;// 不再创建翻译图标
|
|||
|
}
|
|||
|
var text = window.getSelection().toString().trim();
|
|||
|
log('text:' + text);
|
|||
|
if (text && icon.style.display == 'none') {
|
|||
|
log('show icon');
|
|||
|
log(text + '|' + e.pageX + '|' + e.pageY);
|
|||
|
icon.style.top = e.pageY + 10 + 'px';
|
|||
|
icon.style.left = e.pageX + 10 + 'px';
|
|||
|
icon.style.display = 'block';
|
|||
|
} else if (!text) {
|
|||
|
log('hide icon');
|
|||
|
icon.style.display = 'none';
|
|||
|
server.containerDestroy();// 销毁翻译内容面板
|
|||
|
}
|
|||
|
});
|
|||
|
// 翻译图标点击事件
|
|||
|
icon.addEventListener('click', function (e) {
|
|||
|
var text = window.getSelection().toString().trim();
|
|||
|
if (text) {
|
|||
|
log('click:' + text);
|
|||
|
server.containerDestroy();// 销毁翻译内容面板
|
|||
|
// 新建翻译内容面板
|
|||
|
var container = server.container();
|
|||
|
container.style.top = e.pageY + 16 + 'px';
|
|||
|
if (e.pageX + 250 + 16 <= document.body.clientWidth)// container 面板css最大宽度为250px
|
|||
|
container.style.left = e.pageX + 16 + 'px';
|
|||
|
else
|
|||
|
container.style.left = document.body.clientWidth - 250 + 'px';
|
|||
|
document.body.appendChild(container);
|
|||
|
server.rendered.push(container);
|
|||
|
// 判断用户选择的翻译引擎
|
|||
|
var engine = e.target.hasAttribute('type') ? e.target.getAttribute('type') : '';
|
|||
|
log('engine:' + engine);
|
|||
|
switch (engine) {
|
|||
|
case 'bing':
|
|||
|
server.bing(text, container);
|
|||
|
break;
|
|||
|
case 'google':
|
|||
|
server.google(text, container);
|
|||
|
break;
|
|||
|
case 'baidu_translator':
|
|||
|
server.baidu_translator(text, container);
|
|||
|
break;
|
|||
|
default:
|
|||
|
server.youdao(text, container);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
// 翻译server
|
|||
|
var server = {
|
|||
|
// 存放已经生成的翻译内容面板(销毁的时候用)
|
|||
|
rendered: [],
|
|||
|
// 有道翻译 引擎
|
|||
|
youdao: function (text, element) {
|
|||
|
this.ajax('http://dict.youdao.com/w/eng/' + encodeURIComponent(text), function (rst, ele) {
|
|||
|
var parser = new DOMParser(), doc = parser.parseFromString(rst, 'text/html'), html = '';
|
|||
|
var word = doc.querySelector('#phrsListTab .wordbook-js .keyword'),
|
|||
|
pronounce = doc.querySelector('#phrsListTab .wordbook-js .baav'),
|
|||
|
trans = doc.querySelector('#phrsListTab .trans-container'),
|
|||
|
webTrans = doc.querySelectorAll('#tWebTrans .wt-container .title');
|
|||
|
if (!!!pronounce) // 中文拼音
|
|||
|
pronounce = doc.querySelector('#phrsListTab .wordbook-js .phonetic');
|
|||
|
// 排版
|
|||
|
var pos = trans && trans.querySelectorAll('ul li,ul .wordGroup');
|
|||
|
for (var i = 0; pos && i < pos.length; i++) {
|
|||
|
pos[i].innerHTML = pos[i].innerHTML + '▓';
|
|||
|
}
|
|||
|
html += word ? word.innerText.trim() : '';
|
|||
|
html += pronounce ? ' ' + pronounce.innerText.replace(/(\s)+/g, ' ').trim() : '';
|
|||
|
html += trans && trans.querySelector('ul') ? '\n' +
|
|||
|
trans.querySelector('ul').innerText
|
|||
|
.replace(/(\s)+/g, ' ')
|
|||
|
.replace(/(▓)+/g, '\n').trim()
|
|||
|
: '';
|
|||
|
html += trans && trans.querySelector('.additional') ?
|
|||
|
'\n' + trans.querySelector('.additional').innerText.trim().replace(/\n/g, '') : '';
|
|||
|
if (!!webTrans.length) {
|
|||
|
html += '\n网络释义:\n';
|
|||
|
for (var j = 0; j < webTrans.length; j++) {
|
|||
|
if (j !== 0)
|
|||
|
html += ';';
|
|||
|
html += webTrans[j].innerText.replace(/\n/g, '').trim();
|
|||
|
}
|
|||
|
}
|
|||
|
ele.innerText = html;
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, function (rst, ele) {
|
|||
|
ele.innerText = '有道翻译 无法连接!';
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, element);
|
|||
|
},
|
|||
|
// Bing词典 引擎
|
|||
|
bing: function (text, element) {
|
|||
|
this.ajax('http://cn.bing.com/dict/search?q=' + encodeURIComponent(text), function (rst, ele) {
|
|||
|
var parser = new DOMParser(), doc = parser.parseFromString(rst, 'text/html'), html = '';
|
|||
|
var word = doc.querySelector('.hd_area'),
|
|||
|
trans = doc.querySelector('.qdef ul'),
|
|||
|
forms = doc.querySelector('.qdef .hd_if');
|
|||
|
// 排版
|
|||
|
var headword = doc.querySelector('#headword');
|
|||
|
if (headword)
|
|||
|
headword.innerHTML = headword.innerHTML + '<br>';
|
|||
|
var pos = doc.querySelectorAll('.qdef ul li .pos');
|
|||
|
for (var i = 0; i < pos.length; i++) {
|
|||
|
pos[i].innerText = '\n【' + pos[i].innerText + '】';
|
|||
|
}
|
|||
|
html += word ? word.innerText.replace(/\n/g, ' ').trim() : '';
|
|||
|
html += trans ? '\n' + trans.innerText.trim() : '';
|
|||
|
html += forms ? '\n' + forms.innerText.trim() : '';
|
|||
|
ele.innerText = html;
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, function (rst, ele) {
|
|||
|
ele.innerText = 'Bing词典 无法连接!';
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, element);
|
|||
|
},
|
|||
|
// 谷歌翻译 引擎
|
|||
|
google: function (text, element) {
|
|||
|
var apiUrl = 'https://translate.googleapis.com/translate_a/single?client=gtx&dt=t&dt=bd&dj=1&source=input&sl=en&tl=zh-CN&hl=en&q=';
|
|||
|
this.ajax(apiUrl + encodeURIComponent(text), function (rst, ele) {
|
|||
|
var json = JSON.parse(rst), html = '';
|
|||
|
for (var i = 0; i < json.sentences.length; i++) {
|
|||
|
html += json.sentences[i].orig + '\n';
|
|||
|
html += json.sentences[i].trans + '\n';
|
|||
|
}
|
|||
|
ele.innerText = html;
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, function (rst, ele) {
|
|||
|
ele.innerText = '谷歌翻译 无法连接!';
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, element);
|
|||
|
},
|
|||
|
// 百度翻译 引擎
|
|||
|
baidu_translator: function (text, element) {
|
|||
|
var data = new FormData();
|
|||
|
data.set('from', 'en');
|
|||
|
data.set('to', 'zh');
|
|||
|
data.set('query', text);
|
|||
|
this.ajax('http://fanyi.baidu.com/v2transapi', function (rst, ele) {
|
|||
|
var json = JSON.parse(rst), html = '';
|
|||
|
for (var i = 0; i < json.trans_result.data.length; i++) {
|
|||
|
html += json.trans_result.data[i].src + '\n';
|
|||
|
html += json.trans_result.data[i].dst + '\n';
|
|||
|
}
|
|||
|
ele.innerText = html;
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
}, function (rst, ele) {
|
|||
|
ele.innerText = '百度翻译 无法连接!';
|
|||
|
ele.style.display = 'block';// 显示结果
|
|||
|
},
|
|||
|
element,
|
|||
|
'POST',
|
|||
|
data
|
|||
|
);
|
|||
|
},
|
|||
|
// ajax 跨域访问公共方法
|
|||
|
ajax: function (url, success, error, element, method, data, headers) {
|
|||
|
if (!!!method)
|
|||
|
method = 'GET';
|
|||
|
// >>>因为Tampermonkey跨域访问(a.com)时会自动携带对应域名(a.com)的对应cookie
|
|||
|
// 不会携带当前域名的cookie
|
|||
|
// 所以,GM_xmlhttpRequest【不存在】cookie跨域访问安全性问题
|
|||
|
// 以下设置默认headers不起作用<<<
|
|||
|
if (!!!headers)
|
|||
|
headers = {'cookie': ''};
|
|||
|
GM_xmlhttpRequest({
|
|||
|
method: method,
|
|||
|
url: url,
|
|||
|
headers: headers,
|
|||
|
data: data,
|
|||
|
onload: function (res) {
|
|||
|
success(res.responseText, element);
|
|||
|
},
|
|||
|
onerror: function (res) {
|
|||
|
error(res.responseText, element);
|
|||
|
}
|
|||
|
});
|
|||
|
},
|
|||
|
// 销毁已经生成的翻译内容面板
|
|||
|
containerDestroy: function () {
|
|||
|
for (var i = this.rendered.length - 1; i >= 0; i--) {
|
|||
|
if (this.rendered[i] && this.rendered[i].parentNode) {
|
|||
|
this.rendered[i].parentNode.removeChild(this.rendered[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
// 生成翻译结果面板 DOM (此时还未添加到页面)
|
|||
|
container: function () {
|
|||
|
var div = document.createElement('div');
|
|||
|
div.setAttribute('style', '' +
|
|||
|
'display:none!important;' +
|
|||
|
'position:absolute!important;' +
|
|||
|
'font-size:13px!important;' +
|
|||
|
'overflow:auto!important;' +
|
|||
|
'background:#fefee6!important;' +
|
|||
|
'font-family:Arial,sans-serif!important;' +
|
|||
|
'font-weight:normal!important;' +
|
|||
|
'text-align:left!important;' +
|
|||
|
'color:#000!important;' +
|
|||
|
'padding:0.5em 1em!important;' +
|
|||
|
'line-height:1.5em!important;' +
|
|||
|
'border-radius:5px!important;' +
|
|||
|
'border:1px solid #ccc!important;' +
|
|||
|
'max-width:250px!important;' +
|
|||
|
'max-height:150px!important;' +
|
|||
|
'z-index:2147483647!important;' +
|
|||
|
'');
|
|||
|
return div;
|
|||
|
}
|
|||
|
};// 翻译server结束
|
|||
|
})();
|