// ==UserScript== // @namespace ATGT // @name Bing Dict, with pronunciation // @name:zh-CN 必应词典,带英语发音 // @description Translate selected words by Bing Dict, with EN pronunciation, with CN pinyin, translation is disabled by default, check the 'Bing Dict' at bottom left to enable tranlation. // @description:zh-CN 划词翻译,使用必应词典(支持英汉、汉英),带英语发音,带中文拼音,默认不开启翻译,勾选左下角的'Bing Dict'开启翻译。 // @version 1.4.3 // @author StrongOp // @supportURL https://github.com/strongop/user-scripts/issues // @match http://*/* // @match https://*/* // -match https://github.com/* // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js // @grant GM.xmlHttpRequest // @grant GM_xmlhttpRequest // @grant GM.setValue // @grant GM.getValue // @grant GM_setValue // @grant GM_getValue // @connect www.bing.com // @icon https://www.bing.com/favicon.ico // @run-at document-end // @downloadURL none // ==/UserScript== /* eslint: */ /* global GM GM_xmlhttpRequest GM_setValue GM_getValue */ /* Change Log: v1.4.1: 31 Jan 2019, Refactor with class, Add pronunciation support. v1.3.8: 30 Jan 2019, Need user click on checkbox to enable translate. v1.3.5: 21 Dec 2018, Fix GM_xmlhttpRequest not def in Greasemonkey, GM not define in Tampermonkey v1.3.4: 28 Nov 2018, Fix GM.xmlHttpRequest not def in chrome. v1.3.3: 28 Jan 2018, Fix dict provider overlap with result. Add test cases. 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. */ /* if (typeof GM_xmlhttpRequest !== 'undefined') var GM = { setValue: GM_setValue, getValue: GM_getValue }; */ console.log('!!!!!!!!!!!!!!!!!!!!!bing-dict!!!!!!!!!!!!!!!!!!!!!!!!'); let dict_result_id = 'ATGT-bing-dict-result-wrapper'; const DICT_RESULT_CSS = ` div#${dict_result_id}-reset { all: initial; * { all: initial; } } div#${dict_result_id} { all: initial; div, img, a, input, span, ul, li { all: initial; } } div#${dict_result_id} { display: block; position: fixed; left: 2px; bottom: 2px; max-width: 32%; z-index: 2100000000; padding: 0; margin: 0; color: black; background-color: rgba(255,255,255,0.9); font-size: small; font-family: sans-serif; white-space: normal; } div#${dict_result_id} .dict-provider { font-size: xx-small; float: right; margin-left: 0.5rem; /* position: absolute; top: 2px; right: 2px; */ } div#${dict_result_id} .dict-provider input { display: inline; vertical-align: bottom; transform: scale(0.7); margin: 0 0; border: solid 1px; -webkit-appearance: checkbox; } div#${dict_result_id} .search_suggest_area { font-size: xx-small; } div#${dict_result_id} .error { color: red; } div#${dict_result_id} .headword { color: #37a; font-weight: bold; font-size: medium; } div#${dict_result_id} .div_title { font-weight: bold; } div#${dict_result_id} .suggest_word { margin-right: 5px; } div#${dict_result_id} .mach_trans { font-style: italic; font-size: x-small; } div#${dict_result_id} a:link { color: #37a; text-decoration: none; } div#${dict_result_id} a:hover { color: white; background-color: #37a; } div#${dict_result_id} a:visited { color: #37a; } div#${dict_result_id} .pronuce { color: gray; } div#${dict_result_id} .pronuce audio { display: inline; } div#${dict_result_id} .pronuce a { width: initial; height: initial; } div#${dict_result_id} .pronuce a:hover { color: white; background-color: white; } div#${dict_result_id} .mach_trans_result { color: gray; } div#${dict_result_id} ul { list-style-type: none; padding: 1px; margin: 0px; } div#${dict_result_id} ul li{ margin-top: 1px; } div#${dict_result_id} ul li span { float:left; color: white; background-color: gray; text-align: center; padding: 0 2px; margin-right: 3px; } div#${dict_result_id} a img.audioPlayer:hover { opacity: 0.8; } div#${dict_result_id} img.audioPlayer { width: 1em; height: 1em; } `; class DictResultView { //dictResultDiv; constructor(prefs) { this.prefs = prefs; this.addStyleSheet(); this.createDictResultDiv(); this.transEnableShown = false; } addStyleSheet() { let style = document.createElement('STYLE'); style.type = 'text/css'; style.appendChild(document.createTextNode(DICT_RESULT_CSS)); document.head.appendChild(style); } createDictResultDiv() { this.addStyleSheet(); let div_wrapper_reset = document.createElement('DIV'); div_wrapper_reset.id = `${dict_result_id}-reset`; let div = document.createElement('DIV'); div.id = `${dict_result_id}`; div_wrapper_reset.appendChild(div); document.body.appendChild(div_wrapper_reset); this.dictResultDiv = div; } setProvider(provider) { this.dictProvider = provider; } setResult(defs) { this.dictResultDiv.innerHTML = this.dictProvider + defs; this.dictResultDiv.style.display = 'block'; this.showEnableTransChoice(); } hideResult() { this.dictResultDiv.style.display = 'none'; this.transEnableShown = false; } mouseEventInView(event) { // if Mouse is inside result element let divRect = this.dictResultDiv.getBoundingClientRect(); let isInView = (event.clientX >= divRect.left && event.clientX <= divRect.right && event.clientY >= divRect.top && event.clientY <= divRect.bottom); if (isInView) console.log('mouse in result area'); return isInView; } mouseEventInDictProviderBanner(event) { // if Mouse is inside dict provider to enable/disable tranlation try { let divRect = this.dictResultDiv.querySelector(`.dict-provider`).getBoundingClientRect(); let isInView = (event.clientX >= divRect.left && event.clientX <= divRect.right && event.clientY >= divRect.top && event.clientY <= divRect.bottom); if (isInView) console.log('mouse in dict provider banner'); return isInView; } catch (e) { return false; } } showEnableTransChoice() { this.transEnableShown = true; let prefs = this.prefs; let view = this; function enableTransChoiceHandler(event) { console.log('enableTransChoiceHandler called, translate enable ', event.target.checked); prefs.updateTransEnabledList(location.host, !!event.target.checked); prefs.transEnabledOnPage = event.target.checked; if (prefs.transEnabledOnPage && CurrentSelWord.length > 0) setTimeout(bingDict.search.bind(bingDict), 0, CurrentSelWord); else view.hideResult(); } let enableCheckbox = document.querySelector(`div#${dict_result_id} input#enableTrans`); enableCheckbox.checked = this.prefs.transEnabledOnPage; enableCheckbox.onclick = enableTransChoiceHandler; } } var dictCache = {}; var entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; function escapeHtml(string) { return String(string).replace(/[&<>"'`=/]/g, function (s) { return entityMap[s]; }); } class DictProvider { //resultView; constructor(resultView) { this.resultView = resultView; let dictProvider = '
'; this.resultView.setProvider(dictProvider); } search(word) { console.log(`base DictProvider::search ${word}`); return 'not implement yet.'; } } class BingDictProvider extends DictProvider { constructor(resultView) { super(resultView); let dictProvider = ''; this.resultView.setProvider(dictProvider); this.baseURL = 'https://www.bing.com/'; } search(word) { let self = this; /* function playAudio(audioLink) { let audio = new Audio(audioLink); audio.play(); }*/ function parseVoiceLink(elem, id_prefix) { let voiceLink; //let voiceIcon; try { let linkElem = elem.childNodes[0]; //console.log('PronuceLink', linkElem); let handler = linkElem.attributes['onclick'].value; let matches = handler.match(/'(https?:\/\/[^ ']*)'\s*,\s*'([^']*)'/m); //console.log('matches', matches); voiceLink = matches[1]; //voiceIcon = matches[2]; } catch (e) { console.log('parseVoiceLink', e); } let id = `${id_prefix}_${Math.random()}`; let speakerIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAZCAYAAAAv3j5gAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wICACYBOBTr1QAAAvZJREFUSMed1k2IVlUYB/Dfe8dRabqS9mFiEn3ZqQxC2lQU5a6FEBE4tAhaJUqrK0HRxkWLoBNupJA2UlCuigipdtXCwCyirEsLw4omM8aak5jizNui5x1ub+/YzDxwuefc8/F//s/n7VlA+vvv0Nt9HJSctuENNDhUN+3cqDO/70vGZv8Z102r5DQ/rkYdKDnp7T6u5DRectqJ97EBD2BsIeUuXpyDFdhaclpbN+38WjUKpLNhF17BeMxvGHVmIFft+Q4ewqd4tuQ0PmBWdQE6lFeXnPZi39Bd16I3rNiQXBmK7cS2/5huwKLktB3v4fkRSm8cwagX59bG/F28hho7Sk5rYEXJqcKLuB03hS/WLGCd1SPM3C85PYVnSk731k17quR0EE/gMbyAmQrPYQ8exq2XAIHLB4wGviw5TeBJ3IhXY98JfBSstpScehW2d02wBKlKTusxi/04i0dKTtfhZ3wZ++7DWIWblwiwKgLgLnyFl/AWTsf6ZPj7+5hvGQCtWyLQxnhfj6tD4zU42mEAv+H8ICUqS5e50PjzMNEtWN8x1eZ4z+BPXIPecoDmAXEGl0XenIrvg2A6H8+ES2X5ImQlNmE6LuwNBVW/U32Wx6jk1MPdof3X+DXMB390FBnHOfSXBVQ3bR8f420crpt2OoDh23hPhFmn0V+BC4G+WPkrwKbwaDDcgHti/YNOzZvAMcxV+GmJhH4ZUUx3RahP49BQGrQDoKPL8FG3lcBhTOHpumlnSk7ronaKljFbYS9OLhJjFv1OpR/47Aju77DZgAdjfKxu2rkqqN0WBXNrZ/MoudAJ2+F2faJu2tmS0yrsiET9MJiqIoLO4WzdtF/UTTsZLXtqBNDJLlC3j3U/4fEYH6yb9vR8wtZN22186qb9BHfi+NAlZ4aBRv2j4GUcwDsLdOF/t+eS06aS05GSUz+eN0tOKxcZMFX3rmqBhNQ/sFndtD9iEq/H0jcREJeMyLhjrhudvUVqd0X8aHyGH0b45X/lb9cUC+N7uVGjAAAAAElFTkSuQmCC'; let voiceHTML = `