// ==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 = '' +
'必应' +
'谷歌' +
'有道' +
'百度' +
'';
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 + '
';
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结束
})();