修改拖动点按逻辑,面板支持自适应位置,重构金山词霸查词引擎,样式修改

This commit is contained in:
example 2019-02-23 09:28:48 +08:00
parent 526c52e7f9
commit c36d0b3820

View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name 划词翻译:多词典查询 // @name 划词翻译:多词典查询
// @namespace http://tampermonkey.net/ // @namespace http://tampermonkey.net/
// @version 3.0 // @version 3.1
// @description 划词翻译调用“有道词典有道翻译、金山词霸、Bing 词典必应词典、剑桥高阶、沪江小D、谷歌翻译” // @description 划词翻译调用“有道词典有道翻译、金山词霸、Bing 词典必应词典、剑桥高阶、沪江小D、谷歌翻译”
// @author https://github.com/barrer // @author https://github.com/barrer
// @match http://*/* // @match http://*/*
@ -26,7 +26,7 @@
var style = document.createElement('style'); var style = document.createElement('style');
style.textContent = ` style.textContent = `
/*组件样式*/ /*组件样式*/
*{word-wrap:break-word!important} *{word-wrap:break-word!important;word-break:break-word!important}
a{color:#36f;text-decoration:none;cursor:pointer} a{color:#36f;text-decoration:none;cursor:pointer}
a:hover{text-decoration:underline} a:hover{text-decoration:underline}
img{cursor:pointer;display:inline-block;width:22px;height:22px;border:1px solid #dfe1e5;border-radius:22px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle} img{cursor:pointer;display:inline-block;width:22px;height:22px;border:1px solid #dfe1e5;border-radius:22px;background-color:rgba(255,255,255,1);padding:2px;margin:0;margin-right:5px;box-sizing:content-box;vertical-align:middle}
@ -35,24 +35,19 @@
img[activate]{border:1px solid transparent;-webkit-box-shadow:0 0 0 1px #f90;box-shadow:0 0 0 1px #f90} img[activate]{border:1px solid transparent;-webkit-box-shadow:0 0 0 1px #f90;box-shadow:0 0 0 1px #f90}
tr-icon{display:none;position:absolute;padding:2px;margin:0;cursor:move;box-sizing:content-box;font-size:13px;text-align:left;border:0;color:black;z-index:2147483647;background:#fff;border-radius:2px;-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 1px rgba(0,0,0,0.08);box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 1px rgba(0,0,0,0.08)} tr-icon{display:none;position:absolute;padding:2px;margin:0;cursor:move;box-sizing:content-box;font-size:13px;text-align:left;border:0;color:black;z-index:2147483647;background:#fff;border-radius:2px;-webkit-box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 1px rgba(0,0,0,0.08);box-shadow:0 3px 8px 0 rgba(0,0,0,0.2),0 0 0 1px rgba(0,0,0,0.08)}
tr-audio{display:block;margin-bottom:5px} tr-audio{display:block;margin-bottom:5px}
tr-audio a{color:#36f;text-decoration:none;cursor:pointer;margin-right:10px} tr-audio a{margin-right:1em;font-size:80%}
tr-audio a:last-of-type{margin-right:auto} tr-audio a:last-of-type{margin-right:auto}
tr-audio a:hover{text-decoration:underline}
tr-content{display:block;max-width:300px;max-height:200px;width:300px;height:200px;overflow-x:auto;overflow-y:scroll;background:white;padding:2px 8px;margin-top:5px;box-sizing:content-box;font-family:"Helvetica Neue","Helvetica","Arial","sans-serif";font-size:14px;line-height:18px} tr-content{display:block;max-width:300px;max-height:200px;width:300px;height:200px;overflow-x:auto;overflow-y:scroll;background:white;padding:2px 8px;margin-top:5px;box-sizing:content-box;font-family:"Helvetica Neue","Helvetica","Arial","sans-serif";font-size:14px;line-height:18px}
a.audio-button{color:#36f;text-decoration:none;cursor:pointer;margin-right:10px}
a.audio-button:last-of-type{margin-right:auto}
a.audio-button:hover{text-decoration:underline}
.br{border-top:1px dashed #777;margin:.5em auto .3em auto}
.list-title~.list-title{margin-top:1em} .list-title~.list-title{margin-top:1em}
.list-title{color:#00c;display:inline-block;text-decoration:none;cursor:inherit} .list-title{color:#00c;display:inline-block}
.list-title:hover{text-decoration:none;cursor:inherit} .list-title:hover{text-decoration:none}
/*各引擎样式*/ /*各引擎样式*/
.google .sentences,.google .trans,.google .orig,.google .dict,.google .pos,.none{display:block} .google .sentences,.google .trans,.google .orig,.google .dict,.google .pos,.none{display:block}
.google .backend,.google .entry,.google .base_form,.google .pos_enum,.google .src,.google .confidence,.google .ld_result,.none{display:none} .google .backend,.google .entry,.google .base_form,.google .pos_enum,.google .src,.google .confidence,.google .ld_result,.none{display:none}
.google .orig{font-style:italic;color:#777} .google .orig{font-style:italic;color:#777}
.google .pos{margin-top:1em} .google .pos{margin-top:1em}
.google .pos:before{content:"["} .google .pos:before{content:"<"}
.google .pos:after{content:"]"} .google .pos:after{content:">"}
.google .terms:before{content:"【"} .google .terms:before{content:"【"}
.google .terms:after{content:"】"} .google .terms:after{content:"】"}
.google .terms{margin-right:.2em} .google .terms{margin-right:.2em}
@ -60,9 +55,11 @@
.youdao .phone{color:#777;margin-right:1em} .youdao .phone{color:#777;margin-right:1em}
.youdao .phone:before{content:"["} .youdao .phone:before{content:"["}
.youdao .phone:after{content:"]"} .youdao .phone:after{content:"]"}
.youdao .phrs:before{content:"[短语]";display:block} .youdao .pos:before{content:"<"}
.youdao .trs>.tr>.exam:before{content:"[例句]";display:block} .youdao .pos:after{content:">"}
.youdao .trs>.tr>.l:before{content:"[释义]";display:block} .youdao .phrs{display:none}
.youdao .trs>.tr>.exam{display:none}
.youdao .trs>.tr>.l{display:block;margin-left:1em}
.youdao [class="#text"]{font-style:italic} .youdao [class="#text"]{font-style:italic}
.youdao .return-phrase,.youdao [class="@action"],.none{display:none} .youdao .return-phrase,.youdao [class="@action"],.none{display:none}
.hjenglish dl,.hjenglish dt,.hjenglish dd,.hjenglish p,.hjenglish ul,.hjenglish li,.hjenglish h3{margin:0;padding:0;margin-block-start:0;margin-block-end:0;margin-inline-start:0;margin-inline-end:0} .hjenglish dl,.hjenglish dt,.hjenglish dd,.hjenglish p,.hjenglish ul,.hjenglish li,.hjenglish h3{margin:0;padding:0;margin-block-start:0;margin-block-end:0;margin-inline-start:0;margin-inline-end:0}
@ -100,20 +97,15 @@
.cambridge .headword .hw{display:block} .cambridge .headword .hw{display:block}
.cambridge .pron{color:#777;margin-right:1em} .cambridge .pron{color:#777;margin-right:1em}
.cambridge b.def{font-weight:normal} .cambridge b.def{font-weight:normal}
.cambridge .epp-xref{border:1px solid #777;border-radius:.5em;padding:0 2px;font-size:.8em} .cambridge .epp-xref{border:1px solid #777;border-radius:.5em;padding:0 2px;font-size:80%}
.cambridge .examp,.cambridge .extraexamps,.cambridge .cols,.cambridge .xref,.cambridge .fcdo{display:none} .cambridge .examp,.cambridge .extraexamps,.cambridge .cols,.cambridge .xref,.cambridge .fcdo{display:none}
.cambridge .entry-body__el+.entry-body__el{margin-top:1em} .cambridge .entry-body__el+.entry-body__el{margin-top:1em}
.cambridge .pos-body{margin-left:1em} .cambridge .pos-body{margin-left:1em}
.iciba h1{font-size:1em;font-weight:normal} .iciba strong{font-size:1em;font-weight:normal}
.iciba ul,.iciba li{list-style:none;margin:0;padding:0} .iciba p{padding:0;margin:0}
.iciba p,.iciba h1{margin:0;padding:0} .iciba .icIBahyI-footer{display:none}
.iciba p{display:inline} .iciba .icIBahyI-prons{color:#777}
.iciba .base-speak{color:#777} .iciba .icIBahyI-eg{margin-right:1em}
.iciba .base-speak>span{margin-right:1em}
.iciba .base-speak>span:last-of-type{margin-right:auto}
.iciba .change{margin-top:.4em}
.iciba .change span{margin-right:.5em}
.iciba .change span:last-of-type{margin-right:auto}
`; `;
// iframe 工具库 // iframe 工具库
var iframe = document.createElement('iframe'); var iframe = document.createElement('iframe');
@ -193,7 +185,7 @@
engines: (function () { engines: (function () {
var obj = {}; var obj = {};
obj[ids.ICIBA] = function (text, time) { obj[ids.ICIBA] = function (text, time) {
ajax('http://www.iciba.com/' + encodeURIComponent(text), function (rst) { ajax('http://open.iciba.com/huaci_v3/dict.php?word=' + encodeURIComponent(text), function (rst) {
putEngineResult(ids.ICIBA, parseIciba(rst), time); putEngineResult(ids.ICIBA, parseIciba(rst), time);
showContent(); showContent();
}, function (rst) { }, function (rst) {
@ -202,7 +194,7 @@
}); });
}; };
obj[ids.ICIBA_LOWER_CASE] = function (text, time) { obj[ids.ICIBA_LOWER_CASE] = function (text, time) {
ajax('http://www.iciba.com/' + encodeURIComponent(text.toLowerCase()), function (rst) { ajax('http://open.iciba.com/huaci_v3/dict.php?word=' + encodeURIComponent(text.toLowerCase()), function (rst) {
putEngineResult(ids.ICIBA_LOWER_CASE, parseIciba(rst), time); putEngineResult(ids.ICIBA_LOWER_CASE, parseIciba(rst), time);
showContent(); showContent();
}, function (rst) { }, function (rst) {
@ -323,12 +315,11 @@
img.setAttribute('title', obj.name); img.setAttribute('title', obj.name);
img.setAttribute('icon-id', obj.id); img.setAttribute('icon-id', obj.id);
img.addEventListener('mouseup', function () { img.addEventListener('mouseup', function () {
if (!isDrag()) { // 没有拖动鼠标抬起的时候触发点击事件
if (engineId == obj.id) { if (engineId == obj.id) {
return; // 已经是当前翻译引擎 return; // 已经是当前翻译引擎
} }
contentList.innerHTML = ''; // 清空翻译内容列表 contentList.innerHTML = ''; // 清空翻译内容列表
content.style.display = 'block'; // 立马显示翻译面板 displayContent(); // 立马显示翻译面板
content.scrollTop = 0; // 翻译面板滚动到顶端 content.scrollTop = 0; // 翻译面板滚动到顶端
content.scrollLeft = 0; // 翻译面板滚动到左端 content.scrollLeft = 0; // 翻译面板滚动到左端
engineId = obj.id; // 翻译引擎 ID engineId = obj.id; // 翻译引擎 ID
@ -337,7 +328,6 @@
audioEngines = []; // 清空发音引擎 audioEngines = []; // 清空发音引擎
engineResult = {}; // 清空翻译引擎结果集 engineResult = {}; // 清空翻译引擎结果集
obj.trigger(selected, engineTriggerTime); // 启动翻译引擎 obj.trigger(selected, engineTriggerTime); // 启动翻译引擎
}
}); });
icon.appendChild(img); icon.appendChild(img);
}); });
@ -491,12 +481,7 @@
} }
/**是否包含汉字*/ /**是否包含汉字*/
function hasChineseByRange(str) { function hasChineseByRange(str) {
for (var i = 0; i < str.length; i++) { return /[\u4e00-\u9fa5]/ig.test(str);
if (str.charCodeAt(i) >= 0x4E00 && str.charCodeAt(i) <= 0x9FBF) {
return true;
}
}
return false;
} }
/**uuid*/ /**uuid*/
function uuid() { function uuid() {
@ -539,18 +524,27 @@
.replace(/<\/(.+?)>/g, '</' + tag + '>'); .replace(/<\/(.+?)>/g, '</' + tag + '>');
} }
// html 字符串转 DOM // html 字符串转 DOM
function htmlToDom(htmlStr) { function htmlToDom(html) {
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = htmlStr; div.innerHTML = html;
return div; return div;
} }
/**清理 html*/ /**清理 html*/
function cleanHtml(htmlStr) { function cleanHtml(html) {
return htmlStr.replace(/<script[\s\S]*?<\/script>/ig, '') html = html.replace(/<script[\s\S]*?<\/script>/ig, '')
.replace(/<link[\s\S]*?>/ig, '') .replace(/<link[\s\S]*?>/ig, '')
.replace(/<style[\s\S]*?<\/style>/ig, '') .replace(/<style[\s\S]*?<\/style>/ig, '')
.replace(/<img[\s\S]*?>/ig, '') .replace(/<img[\s\S]*?>/ig, '');
.replace(/on[a-z]*=".*?"/ig, ''); html = cleanAttr(html, 'on[a-z]*');
return html;
}
/**
* 清理指定属性忽略大小写
* @param attr 支持正则表示on[a-z]*表示清理on开头的属性onclickonmove等
*/
function cleanAttr(html, attr) {
var regex = ' ' + attr + '="([^"<>]*)"';
return html.replace(new RegExp(regex, 'ig'), '');
} }
/**带异常处理的 createObjectURL*/ /**带异常处理的 createObjectURL*/
function createObjectURLWithTry(blob) { function createObjectURLWithTry(blob) {
@ -612,7 +606,7 @@
var rst = true; var rst = true;
var count = 0; var count = 0;
idsType.forEach(function (id) { idsType.forEach(function (id) {
if (count < 1) { // 判断指定数量的完成情况 if (count < 0) { // 判断指定数量的完成情况
count++; count++;
if (!(id in engineResult)) { if (!(id in engineResult)) {
log('isAllDone(not found):' + id); log('isAllDone(not found):' + id);
@ -623,6 +617,43 @@
return rst; return rst;
} }
/**显示内容面板*/ /**显示内容面板*/
function displayContent() {
var panelHeight = 241 + 8; // icon 展开后总高度
var panelWidth = 320 + 8; // icon 展开后总宽度
// 计算位置
log('content position:',
'window.scrollY', window.scrollY,
'document.documentElement.scrollTop', document.documentElement.scrollTop,
'document.body.scrollTop', document.body.scrollTop,
'window.innerHeight', window.innerHeight,
'document.documentElement.clientHeight', document.documentElement.clientHeight,
'document.body.clientHeight', document.body.clientHeight,
'icon.style.top', icon.style.top,
'window.scrollX', window.scrollX,
'document.documentElement.scrollLeft', document.documentElement.scrollLeft,
'document.body.scrollLeft', document.body.scrollLeft,
'window.innerWidth', window.innerWidth,
'document.documentElement.clientWidth', document.documentElement.clientWidth,
'document.body.clientWidth', document.body.clientWidth,
'icon.style.left', icon.style.left
);
if (parseInt(icon.style.top) < document.documentElement.scrollTop) {
log('Y adjust top');
icon.style.top = parseInt(document.documentElement.scrollTop) + 'px';
} else if (parseInt(icon.style.top) + panelHeight > document.documentElement.scrollTop + document.documentElement.clientHeight) {
log('Y adjust bottom');
icon.style.top = parseInt(document.documentElement.scrollTop + document.documentElement.clientHeight - panelHeight) + 'px';
}
if (parseInt(icon.style.left) < document.documentElement.scrollLeft) {
log('X adjust left');
icon.style.left = parseInt(document.documentElement.scrollLeft) + 'px';
} else if (parseInt(icon.style.left) + panelWidth > document.documentElement.scrollLeft + document.documentElement.clientWidth) {
log('X adjust right');
icon.style.left = parseInt(document.documentElement.scrollLeft + document.documentElement.clientWidth - panelWidth) + 'px';
}
content.style.display = 'block';
}
/**内容面板填充数据*/
function showContent() { function showContent() {
log('showContent:', idsType, engineResult); log('showContent:', idsType, engineResult);
if (isAllDone()) { if (isAllDone()) {
@ -634,11 +665,11 @@
// 发音 // 发音
audioEngines = []; // 清空防止多次渲染 audioEngines = []; // 清空防止多次渲染
audioEngines.push({ audioEngines.push({
name: '♪us', name: '♪US',
url: 'http://dict.youdao.com/dictvoice?audio=' + selected + '&type=2' url: 'http://dict.youdao.com/dictvoice?audio=' + selected + '&type=2'
}); });
audioEngines.push({ audioEngines.push({
name: '♪uk', name: '♪UK',
url: 'http://dict.youdao.com/dictvoice?audio=' + selected + '&type=1' url: 'http://dict.youdao.com/dictvoice?audio=' + selected + '&type=1'
}); });
var audio = document.createElement('tr-audio'); var audio = document.createElement('tr-audio');
@ -680,8 +711,6 @@
title.setAttribute('rel', 'noreferrer noopener'); title.setAttribute('rel', 'noreferrer noopener');
title.setAttribute('target', '_blank'); title.setAttribute('target', '_blank');
title.setAttribute('href', href); title.setAttribute('href', href);
// 事件
title.addEventListener('click', linkEvent);
contentList.appendChild(title); contentList.appendChild(title);
} }
contentList.appendChild(engineResult[id]); contentList.appendChild(engineResult[id]);
@ -715,9 +744,6 @@
} }
/**发音*/ /**发音*/
function play(obj) { function play(obj) {
if (isDrag()) { // 拖动时候不触发发音
return;
}
var audio = new iframeWin.Audio(); var audio = new iframeWin.Audio();
ajax(obj.url, function (rst, res) { ajax(obj.url, function (rst, res) {
audio.src = createObjectURLWithTry(res.response); audio.src = createObjectURLWithTry(res.response);
@ -739,12 +765,6 @@
}); });
return type; return type;
} }
/**链接点击事件*/
function linkEvent(e) {
if (isDrag()) { // 拖动时候不触发跳转
e.preventDefault();
}
}
/**有道词典排版*/ /**有道词典排版*/
function parseYoudao(rst) { function parseYoudao(rst) {
var html = ''; var html = '';
@ -808,16 +828,32 @@
var dom = document.createElement('div'); var dom = document.createElement('div');
dom.setAttribute('class', ids.ICIBA); dom.setAttribute('class', ids.ICIBA);
try { try {
rst = /dict.innerHTML='(.*?)';\n \tdict.style.display = "block";/g.exec(rst)[1];
rst = rst
.replace(/\\"/g, '"')
.replace(/\\'/g, '\'')
.replace(/onclick=/g, 'data-onclick=');
rst = cleanAttr(rst, 'style');
// 标识符处理
var symbolRegex = /(<span class="icIBahyI-fl">.*?(?: xml:lang=).*?<\/span>)/ig;
var symbolMatch;
var symbolResult = [];
while ((symbolMatch = symbolRegex.exec(rst)) != null) {
symbolResult.push(symbolMatch[1]);
}
symbolResult.forEach(function (str) {
rst = rst.replace(str,
str.replace(/\[英\]/g, '英')
.replace(/\[美\]/g, '美')
);
});
rst = cleanHtml(rst) rst = cleanHtml(rst)
.replace(/(?:a>)/ig, 'span>') .replace(/(?:a>)/ig, 'span>')
.replace(/(?:<a)/ig, '<span') .replace(/(?:<a)/ig, '<span');
.replace(/style=".*?"/ig, '')
.replace(/<span class="prop">释义<\/span>/ig, '<span class="prop">释义:<\/span>')
.replace(/<h1 class="base-word abbr chinese change-base">变形<\/h1>/ig, '');
var doc = htmlToDom(rst); var doc = htmlToDom(rst);
// 发音 // 发音
doc.querySelectorAll('[ms-on-mouseover]').forEach(function (ele) { doc.querySelectorAll('[title="真人发音"],[title="机器发音"]').forEach(function (ele) {
var str = ele.getAttribute('ms-on-mouseover'); var str = ele.getAttribute('data-onclick');
var regex = /'(http:\/\/.*?)'/ig; var regex = /'(http:\/\/.*?)'/ig;
var match = regex.exec(str); var match = regex.exec(str);
if (match && match.length >= 1) { if (match && match.length >= 1) {
@ -828,9 +864,7 @@
} }
}); });
// 内容 // 内容
doc.querySelectorAll('.in-base .in-base-top,.in-base .base-list,.in-base .change').forEach(function (ele) { dom.appendChild(doc);
dom.appendChild(ele);
});
} catch (error) { } catch (error) {
log(error); log(error);
dom.appendChild(htmlToDom(error)); dom.appendChild(htmlToDom(error));