// ==UserScript==
// @namespace ATGT
// @name bing-dict
// @name:zh-CN 必应词典
// @description Select any word in any web to show it's definition from Bing Dict.
// @description:zh-CN 划词翻译,使用必应词典
// @version 1.3.2
// @match http://*/*
// @match https://*/*
// -match https://github.com/*
// @grant GM.xmlHttpRequest
// @connect www.bing.com
// @icon https://www.bing.com/favicon.ico
// @run-at document-end
// ==/UserScript==
/*
Change Log:
v1.3.2:
27 Jan 2018, Add dict provider name: Bing Dict
v1.3.1:
19 Jan 2018, Escape suggested words
v1.3:
19 Jan 2018, refactor & parse search suggestion
v1.2:
14 Jan 2018, Escape search word and result.
v1.1:
13 Jan 2018, Reset style for result div.
v1.0:
12 Jan 2018, Initial version.
*/
console.log("!!!!!!!!!!!!!!!!!!!!!bing-dict!!!!!!!!!!!!!!!!!!!!!!!!");
(function () {
(function addStyleSheet() {
var style = document.createElement("STYLE");
style.type = "text/css";
var css = `
div#ATGT-bing-dict-result-wrapper-reset {
all: initial;
* {
all: initial;
}
}
div#ATGT-bing-dict-result-wrapper {
display: block;
position: fixed;
left: 2px;
bottom: 2px;
max-width: 30%;
z-index: 2100000000;
padding: 0;
margin: 0;
color: black;
background-color: rgba(255,255,255,0.9);
font-size: small;
font-family: sans-serif;
}
div#ATGT-bing-dict-result-wrapper .dict-provider {
font-size: xx-small;
position: absolute;
top: 2px;
right: 2px;
}
div#ATGT-bing-dict-result-wrapper .search_suggest_area {
font-size: xx-small;
}
div#ATGT-bing-dict-result-wrapper .error {
color: red;
}
div#ATGT-bing-dict-result-wrapper .headword {
font-weight: bold;
font-size: medium;
}
div#ATGT-bing-dict-result-wrapper .div_title {
font-weight: bold;
}
div#ATGT-bing-dict-result-wrapper .suggest_word {
margin-right: 5px;
}
div#ATGT-bing-dict-result-wrapper .mach_trans {
font-style: italic;
font-size: x-small;
}
div#ATGT-bing-dict-result-wrapper a:link {
color: #37a;
text-decoration: none;
}
div#ATGT-bing-dict-result-wrapper a:hover {
color: white;
background-color: #37a;
}
div#ATGT-bing-dict-result-wrapper .pronuce {
color: gray;
}
div#ATGT-bing-dict-result-wrapper .mach_trans_result {
color: gray;
}
div#ATGT-bing-dict-result-wrapper ul {
list-style-type: none;
padding: 1px;
margin: 0px;
}
div#ATGT-bing-dict-result-wrapper ul li{
margin-top: 1px;
}
div#ATGT-bing-dict-result-wrapper ul li span {
float:left;
color: white;
background-color: gray;
text-align: center;
padding: 0 2px;
margin-right: 3px;
}
`;
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
})();
var dictResultDiv = (function createDictResultDiv() {
var div_wrapper_reset = document.createElement("DIV");
div_wrapper_reset.id = "ATGT-bing-dict-result-wrapper-reset";
var div = document.createElement("DIV");
div.id = "ATGT-bing-dict-result-wrapper";
div_wrapper_reset.appendChild(div);
document.body.appendChild(div_wrapper_reset);
return div;
})();
function setResult(defs) {
dictResultDiv.innerHTML = defs;
}
var dictCache = {};
var lastSearchWord = "";
var entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
};
function escapeHtml (string) {
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
return entityMap[s];
});
}
function parseDefinition(page, url) {
//console.log("parseDefinition");
var qdef = page.querySelector(".qdef");
//console.log("qdef ", qdef);
var hd_area = qdef.childNodes[0];
try {
var headword = escapeHtml(hd_area.querySelector("#headword").innerText);
} catch (e) {
var headword = "";
}
var hd_pr = "";
try { /* en to cn */
hd_pr = escapeHtml(hd_area.querySelector(".hd_prUS").innerText)
+ " " + escapeHtml(hd_area.querySelector(".hd_pr").innerText);
} catch (e) {
}
try {
if (!hd_pr) /* cn to en */
hd_pr = escapeHtml(hd_area.querySelector(".hd_tf_lh").innerText);
} catch (e) {
}
headword = "<div class='headword'>"
+ "<a href='"+url+"' target='_blank'>" + headword + "</a>"
+ "</div>";
hd_pr = "<div class='pronuce'>" + hd_pr + "</div>";
try {
var def_area = qdef.childNodes[1];
var def_list = def_area.querySelectorAll("li");
var defs = "<ul>";
for (var def of def_list) {
defs += "<li><span>" + escapeHtml(def.childNodes[0].innerText) + "</span>"
+ escapeHtml(def.childNodes[1].innerText) + "</li>";
}
defs += "</ul>";
} catch (e) {
var defs = "";
}
return headword + hd_pr + defs;
}
function parseMachTrans(page, url) {
//console.log("parseMachTrans");
var trans_area = page.querySelector(".lf_area");
try {
var smt_hw_elem = trans_area.querySelector(".smt_hw");
var smt_hw = escapeHtml(smt_hw_elem.innerText);
var headword_elem = smt_hw_elem.nextElementSibling;
var headword = escapeHtml(headword_elem.innerText);
var trans_result_elem = headword_elem.nextElementSibling;
var trans_result = escapeHtml(trans_result_elem.innerText);
} catch (e) {
}
smt_hw = "<div class='mach_trans'>" + smt_hw + "</div>";
headword = "<div class='headsentence'>"
+"<a href='"+url+"' target='_blank'>" + headword + "</a>"
+"</div>";
trans_result = "<div class='mach_trans_result'>" + trans_result + "</div>";
return smt_hw + headword + trans_result;
}
function parseBingDymArea(dym) {
var suggest = escapeHtml(dym.querySelector(".df_wb_a").innerText);
suggest = "<div class='div_title'>"+suggest+"</div>";
var defs = "<ul>";
for (var s of dym.querySelectorAll(".df_wb_c")) {
var r0 = s.childNodes[0];
var r1 = s.childNodes[1];
defs += "<li><a class='suggest_word' href='"
+ "//www.bing.com" + r0.pathname + r0.search + "'>"
+ escapeHtml(r0.innerText) + "</a>"
+ escapeHtml(r1.innerText) + "</li>";
}
defs += "</ul>";
return suggest + defs;
}
function parseSearchSuggest(page, url) {
//console.log("parseSearchSuggest");
var trans_area = page.querySelector(".lf_area");
var headword = escapeHtml(trans_area.querySelector(".dym_p").innerText);
var suggest = escapeHtml(trans_area.querySelector(".p2-2").innerText);
headword = "<div class='headword'><a href='" + url + "'>" + headword + "</a></div>";
suggest = "<div class='div_title'>" + suggest + "</div>";
var defs = "";
for (var dym of trans_area.querySelectorAll(".dym_area")) {
defs += parseBingDymArea(dym);
}
return "<div class='search_suggest_area'>" + headword + suggest + defs + "</div>";
}
function parseDictResultDom(page, url) {
//console.log("page ", page);
var qdef = page.querySelector(".qdef");
var smt_hw = page.querySelector(".smt_hw");
var search_suggest = page.querySelector(".dym_area") && page.querySelector(".df_wb_c");
if (qdef)
return parseDefinition(page, url);
else if (smt_hw)
return parseMachTrans(page, url);
else if (search_suggest)
return parseSearchSuggest(page, url);
else
return "";
}
const fallback_message = "Try <a href='https://www.bing.com/translator' target=_blank>Microsoft Translator</a>.";
function parseDictResult(word, response) {
//console.log("search dict ok", response);
var url = response.finalUrl;
try {
var parser = new DOMParser();
var doc = parser.parseFromString(response.responseText, "text/html");
var defs = parseDictResultDom(doc, url);
defs = '<div class=dict-provider>Bing Dict</div>' + defs;
dictCache[word] = defs;
} catch (e){
console.log("parseDictResult failed:\n " + e.stack);
var defs = "<span class='error'>Error</span> parsing result of <a href='" + url + "'>"
+ escapeHtml(word) + "</a>, <br />" + fallback_message;
}
setResult(defs);
}
function searchDictFail(word, response) {
//console.log("search dict fail ", response);
var url = response.finalUrl;
var status = (response.status ? response.status : "")
+ " " + (response.statusText ? response.statusText : "");
setResult("<span class='error'>Error</span> searching <a href='" + url + "'>" + escapeHtml(word) + "</a>, "
+ status + "<br />"
+ fallback_message);
}
function searchBingDict(word) {
word = word.replace(/^\s*|\s*$/g, "");
if (word.length == 0) {
setResult("");
lastSearchWord = "";x
return;
}
if (word in dictCache && dictCache[word]) {
console.log("cache hit \"", word, "\"");
if (lastSearchWord != word) {
setResult(dictCache[word]);
lastSearchWord = word;
}
return;
} else {
console.log("cache miss");
}
var url = "http://www.bing.com/dict/search?q=" + encodeURIComponent(word);
console.log(url);
setResult("Searching <a href='" + url + "'>" + escapeHtml(word) + "</a>");
GM.xmlHttpRequest({
url : url,
method : "GET",
onload : (response) => parseDictResult(word, response),
onerror : (response) => searchDictFail(word, response),
});
}
document.addEventListener("mouseup", function (event) {
var divRect = dictResultDiv.getBoundingClientRect();
if (event.clientX >= divRect.left && event.clientX <= divRect.right &&
event.clientY >= divRect.top && event.clientY <= divRect.bottom) {
// Mouse is inside result element, do nothing.
return;
}
var sel = window.getSelection().toString();
console.log("selected: \"", sel, "\"");
searchBingDict(sel);
});
})();
console.log("!!!!!!!!!!!!!!!!!!!!!/bing-dict!!!!!!!!!!!!!!!!!!!!!!!!");