// ==UserScript== // @name 英语阅读助手 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 使用在线翻译,获取单词解释,然后便于在网页上学习英语,而不是简单的使用翻译工具或插件整体翻译 // @author lavaf // @match http://127.0.0.1:8848/TestyoudaoTranslate/Pages/testyouhou.html // @match http://www.51voa.com/* // @grant none // @require https://cdn.jsdelivr.net/gh/emn178/js-sha256/build/sha256.min.js // @require https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js // @downloadURL none // ==/UserScript== (function() { 'use strict'; var appKey = ''; var Secret = ''; //注意:暴露appSecret,有被盗用造成损失的风险 var from_l = 'en'; var to = 'zh-CHS'; var no_translate = ['a', 'an', 'of', 'some', 'the', 'than', 'would', 'this', 'to', 'rather', 'may', 'women', 'many', 'in', 'that', 'have', 'his', 'he', 'her', 'i', 'me', 'they', 'it', 'it\'s', 'myself', 'herself', 'for', 'on', 'has', 'first', 'second', 'am', 'is', 'are', 'were', 'did', 'do', 'does', 'be', 'us', 'was', 'will', 'any', 'other', 'from', 'and', 'him', 'no', 'not', 's', 'n', 'you', 'or', 'where', 'there', 'so', 'where', 'done', 'with', 'at', 'she' ] //不需要翻译短语 var no_idom = ['of'] var textPanel; //全局的单词列表面板对象 var result_count = 0; //已经获得的单词的数量 var result_table = {} //全部已经获得的单词 var deleted_table = {} // 已经删除不再显示的单词 var speech_table = {} //存放音标和发音 var loadJsonCallBack = {} var setting_item_name = ['show_delete_word_button', 'show_ph', 'show_ph_button'] var setting = { 'show_delete_word_button': false, 'show_ph': false, 'show_ph_button': false }; var style = { controlpanel: { style: {}, child: { 'move-button': {}, 'start-button': {} } } } var adw = new addWin_fuzhengyin('50px', '100px'); /* 加载存储在local storage 中的数据 */ if (localStorage) { var a = [ 'result-table', 'speech-table', 'deleted-table', 'setting' ] var objects = [null, null, null, null] for (let var1 in a) { let current = a[var1] var result_temp = localStorage.getItem(current); if (result_temp != null) { objects[var1] = JSON.parse(result_temp); console.log("从localStorage中加载缓存" + current); adw.show("从localStorage中加载缓存" + current); } } if (objects[0] != null) result_table = objects[0] if (objects[1] != null) speech_table = objects[1] if (objects[2] != null) deleted_table = objects[2] if (objects[3] != null) setting = objects[3] } /* 获取已经保存的单词数目 */ for (let s in result_table) { result_count++; } var offestX, offestY; var movable = false; var button = getButton(); setOnClick(button, 1); var panel = createControlPanel(); panel.appendChild(button); document.body.appendChild(panel); var audio = document.createElement("audio"); audio.src = ""; document.body.appendChild(audio); var style_element=document.createElement('style'); document.head.appendChild(style_element); style_element.innerText='#lavaf-start-get-word-button{color:red}' /** * 创建控制面板 */ function createControlPanel() { let div = document.createElement("div"); div.style.id='lavaf-control-panel'; div.style.position = "absolute"; div.style.left = "10px"; div.style.top = "100px"; div.style.border = "#000000 solid 1px"; div.style.padding = "10px"; var move_button = document.createElement("button"); move_button.id='lavaf-move-button'; move_button.innerText = "◉"; move_button.style.marginRight = "10px"; move_button.onmousedown = function(e) { offestX = e.clientX - div.offsetLeft; offestY = e.clientY - div.offsetTop; movable = true; } appendMouseMoveEvents(div) var saved_count_panel = document.createElement("div"); saved_count_panel.id='lavaf-show-saved-word-num'; saved_count_panel.innerText = `保存的单词:${result_count}` div.appendChild(move_button); div.appendChild(saved_count_panel); //获取上次的选择 var show_type_last_selection = "title"; if (localStorage) { var save_show_type = localStorage.getItem("save-show-type"); if (save_show_type != null) { show_type_last_selection = save_show_type; } } var selection_type_show = document.createElement("select"); selection_type_show.id = "selection-type-show"; var option1 = document.createElement("option"); option1.id='lavaf-selection-type-option-item-1'; option1.className='lavaf-selection-type-option-item'; option1.value = "title"; option1.innerText = "title"; var option2 = document.createElement("option"); option2.id='lavaf-selection-type-option-item-2'; option2.className='lavaf-selection-type-option-item'; option2.value = "fixed"; option2.innerText = "fixed"; if (show_type_last_selection === "title") { option1.selected = "selected"; } else { option2.selected = "selected"; } selection_type_show.appendChild(option1); selection_type_show.appendChild(option2); div.appendChild(selection_type_show); var mutil_container = document.createElement("div"); mutil_container.id='lavaf-setting-multiple-select-container'; var need_show_component = document.createElement("select"); need_show_component.id='lavaf-setting-multiple-select'; need_show_component.multiple='multiple'; need_show_component.onchange=function(){ let nodes=need_show_component.childNodes; if(nodes[3].selected){ for (var i = 0; i < nodes.length-1; i++) { nodes[i].selected=false; nodes[i].blur(); for (let s of setting_item_name) { setting[s] = false; } } }else{ for (var i = 0; i < nodes.length-1; i++) { setting[setting_item_name[index]]=nodes[i].selected; } } localStorage.setItem('setting', JSON.stringify(setting)) } var component_array = ["显示删除单词按钮", "显示音标", "显示发音按钮", "无"] for (let i in component_array) { let item=component_array[i] var option_show_delete_word = document.createElement("option"); option_show_delete_word.id='lavaf-setting-multiple-select-option-'+item; option_show_delete_word.className='lavaf-setting-multiple-select-option-item'; option_show_delete_word.value = item; option_show_delete_word.innerText = item need_show_component.appendChild(option_show_delete_word); if(item!=3){ var item_selected = setting[setting_item_name[i]] option_show_delete_word.selected=item_selected; if (item_selected) { option_show_delete_word.focus() } else { // option_show_delete_word.blur } } } need_show_component.multiple = "multiple"; mutil_container.appendChild(need_show_component); div.appendChild(mutil_container); //显示result-table 面板 var result_table_panel = document.createElement("button"); result_table_panel.id='lavaf-show-result-table-panel-button'; result_table_panel.innerText = "显示全部单词"; result_table_panel.onclick = function() { let r = ""; for (let var1 in result_table) { if (deleted_table[var1] == null) r += getWordListItem(var1); } showTextPanel(r); } div.appendChild(result_table_panel) div.appendChild(document.createElement('br')); var show_deleted_word = document.createElement("button"); show_deleted_word.id='lavaf-show-deleted-word-button'; show_deleted_word.innerText = "显示已删除单词"; show_deleted_word.onclick = function() { let r = ''; for (let var1 in deleted_table) { r += getWordListItem(var1); r += "

" + JSON.stringify(deleted_table[var1]) + "

"; } showTextPanel(r); } div.appendChild(show_deleted_word); div.appendChild(document.createElement('br')); return div; } /** * 有道提供 * @param {Object} input 要查询的单词 */ function getInput(input) { if (input.length == 0) { return null; } var result; var len = input.length; if (len <= 20) { result = input; } else { var startStr = input.substring(0, 10); var endStr = input.substring(len - 10, len); result = startStr + len + endStr; } return result; } /** * 为可移动的元素添加鼠标移动的事件 * @param {Object} div 需要操作的元素 */ function appendMouseMoveEvents(div){ div.onmousemove=function(e){ if (movable) { var move_x = e.clientX - offestX; var move_y = e.clientY - offestY; div.style.top = move_y + "px"; div.style.left = move_x + "px"; } } div.onmouseup=function(){ movable=false; } } /** * 创建显示单词列表的面板 * @param {Object} str 要显示的html 内容 */ function createTextPanel(str) { var div = document.createElement("div"); var inner_button = document.createElement("button") inner_button.innerText = "◍"; div.appendChild(inner_button) inner_button.onmousedown = function(e) { offestX = e.clientX - div.offsetLeft; offestY = e.clientY - div.offsetTop; movable = true; } appendMouseMoveEvents(div); var close_button = document.createElement("button"); close_button.id='text-panel-close-button'; close_button.innerText = "X"; div.appendChild(close_button) close_button.onclick = function() { textPanel.style.display='none'; } var inner_dix = document.createElement("div"); div.style.position = "absolute"; div.style.top = (100 + 10) + "px"; div.style.left = (100 + 10) + "px"; div.style.backgroundColor = "lightgray"; div.style.color = "black"; inner_dix.style.padding = "10px"; inner_dix.innerHTML = str; div.appendChild(inner_dix) return div; } function addWin_fuzhengyin(left, top) { this.timeout; this.win; this.delay_move = function() { this.timeout = setTimeout(() => { document.body.removeChild(this.win); this.win = null; }, 2000) } this.show = function(msg) { if (this.win != null && this.win != undefined) { clearTimeout(this.timeout); this.win.innerText = msg; this.delay_move() } else { this.win = document.createElement('div'); this.win.className = 'fuzhengyin-message'; this.win.style.position = 'absolute'; this.win.style.top = top || '100px'; this.win.style.left = left || '100px'; this.win.innerText = msg; this.win.style.backgroundColor = 'lightgreen'; this.win.style.paddingLeft = '15px'; this.win.style.paddingRight = '15px'; this.win.style.paddingTop = '5px'; this.win.style.paddingBottom = '5px'; document.body.appendChild(this.win); this.delay_move() } } } /** * 给p 添加title ,或者设置click事件 * @param {Object} word_table * @param {Object} current_element */ function addTitleOrText(word_table, current_element) { let result = ""; var current_selection_index = document.getElementById("selection-type-show").selectedIndex if (current_selection_index == 0) { for (let var1 in word_table) { if (result_table[word_table[var1]] != null) result += word_table[var1] + ":" + result_table[word_table[var1]] + "\n"; } current_element.title = result; localStorage.setItem("save-show-type", "title") } else { current_element.onclick = function() { showTextPanel(getResult(word_table)); } } } function getResult(word_table){ let result=''; for (let var1 in word_table) { //显示文本面板 let word_name = word_table[var1]; if (result_table[word_name] != null) { // console.log(word_name+" "+deleted_table[word_name]) if (deleted_table[word_name] == null){ result += getWordListItem(word_name); // console.log(result) } } } // showTextPanel(result) return result } /** * 显示单词列表框 * @param {Object} result */ function showTextPanel(result) { if (textPanel == null) { textPanel = createTextPanel(result); document.body.appendChild(textPanel); localStorage.setItem("save-show-type", "fixed") } else { //如果不为空就显示 var currentTextPanelStatus=textPanel.style.display; textPanel.childNodes[textPanel.childNodes.length-1].innerHTML=result if(currentTextPanelStatus=='none'){ textPanel.style.display='block'; } } } /** * 将单词添加到已删除列表 * @param {string} word_name 需要删除的单词 */ function delete_word(word_name) { if (deleted_table[word_name] == null) { let t = new Date(); deleted_table[word_name] = { 'date': Date(), 'm': t.getTime() } } else { delete deleted_table[word_name] } localStorage.setItem('deleted-table', JSON.stringify(deleted_table)) } /** * 获取单词列表详情 * @param {string} word_name 获取单词的解释 */ function getWordListItem(word_name) { var ukph = speech_table[word_name]['uk-ph'] var usph = speech_table[word_name]['us-ph'] var setting_1 = setting[setting_item_name[1]] var setting_2 = setting[setting_item_name[2]] var setting_0 = setting[setting_item_name[0]] return '
' + word_name + "" + (setting_1 ? '【' + speech_table[word_name]['ph'] + '】' : '') + (setting_1 ? '[' + (ukph == undefined ? 'x' : ukph) + ']' : '') + (setting_2 ? '' : '') + (setting_1 ? '[' + (usph == undefined ? 'x' : usph) + ']' : '') + (setting_2 ? '' : '') + ":" + result_table[word_name] + (setting_0 ? '' : '') + "
"; } /** * 播放音频 * @param {Object} src 音频连接 */ function play(src) { audio.src = src; audio.play() } /** * 查看当前需要索引的单词是否都已经查找到,如果是那就开始显示 * @param {Object} word_table * @param {Object} current_element */ function addTitleForP(word_table, current_element) { for (var m = 0; m < word_table.length; m++) { if (result_table[word_table[m]] == undefined) { //还有没完成的查询 return; } } if (m == word_table.length) { //所有单词都完成了查询 localStorage.setItem("result-table", JSON.stringify(result_table)); //保存数据 localStorage.setItem("speech-table", JSON.stringify(speech_table)); addTitleOrText(word_table, current_element); adw.show("单词全部获得解释,可以开始使用了") } } /** * 为开始获取单词按钮设置事件 * @param {Object} button * @param {Object} type */ function setOnClick(button, type) { button.onclick = function() { var p_array = document.getElementsByTagName("p"); //遍历所有的 p 标签 for (var i = 0; i < p_array.length; i++) { let current_element = p_array[i]; // 当前p 标签对象 let p_text = p_array[i].innerText // 字符串 存储当前p 标签的内容 let p_inner = p_text.split(/[\s,"']/); //数组 存储当前p 标签的每一个单词 let word_table = [] //数组 存储需要索引的全部单词 for (var j = 0; j < p_inner.length; j++) { //遍历每一个单词 let item_query = p_inner[j]; if (item_query.trim() === "" || item_query.trim() === "-") { continue; } var last_char = item_query[item_query.length - 1]; if (last_char === ',' || last_char === '.' || last_char === '\'' || last_char === ')') { item_query = item_query.substring(0, item_query.length - 1) } if (item_query.lastIndexOf("'s") >= 0) { item_query = item_query.substring(0, item_query.length - 2) } for (var k = 0; k < no_translate.length; k++) { if (no_translate[k] === item_query.toLowerCase()) { break; } } if (k == no_translate.length) { //需要翻译 word_table.push(item_query) console.log("当前操作:" + item_query); if (type == 1) { var salt = (new Date).getTime(); //随机数 var curtime = Math.round(new Date().getTime() / 1000); var str1 = appKey + getInput(item_query) + salt + curtime + Secret; var sign = sha256(str1); let current_index = j; if (result_table[item_query] == null || result_table[item_query] == undefined) { var s = document.createElement('script'); s.src = 'http://openapi.youdao.com/api?callback=loadJsonCallBack.callback' + current_index + `&q=${item_query}&appKey=${appKey}&salt=${salt}&from=${from_l}&to=${to}&curtime=${curtime}&sign=${sign}&signType=v3`; document.body.appendChild(s); loadJsonCallBack["callback" + current_index] = function(data) { console.log(data); // document.body.removeChild(s); console.log("联网获取到" + item_query + "的翻译"); //完成查询时会把数据放到result-table中 if (data.basic != null && data.basic.explains != null) { let explains = data.basic.explains; let r = `[${data.translation}],${JSON.stringify(explains)};\n`; result_table[item_query] = r; let current_speech = speech_table[item_query]; if (current_speech == null) { speech_table[item_query] = { 'uk': data.basic['uk-speech'], 'us': data.basic['us-speech'], 'us-ph': data.basic['us-phonetic'], 'uk-ph': data.basic['uk-phonetic'], 'ph': data.basic['phonetic'] } } } else { if (data.translation != undefined) { let r = "[" + data.translation + "]\n"; result_table[item_query] = r; } else { console.log("item_query:" + item_query); console.log(data); } } addTitleForP(word_table, current_element); } } else { console.log("已获取" + item_query); if (j == p_inner.length - 1) { addTitleForP(word_table, current_element); } } //查找调用完毕 } //查找调用类型配置完毕 } //翻译调用结束 } //遍历单词结束 //遍历完整个段落 } //遍历段落结束 } //监听函数设置完毕 } /** * 创建按钮 */ function getButton() { var btn_start = document.createElement("button"); btn_start.id='lavaf-start-get-word-button' btn_start.value = "开始"; btn_start.type = "button"; btn_start.innerText = "开始"; return btn_start; } })();