点击开始,获取p 标签的单词,然后获取解释,鼠标悬停到段落便可以显示解释,也可以选择fixed 模式,然后点击段落,便可以显示单词列表,可以通过设置style_element 的内容设置此脚本面板的样式
// ==UserScript== // @name 英语阅读助手 // @namespace http://tampermonkey.net/ // @version 0.7 // @description 点击开始,获取p 标签的单词,然后获取解释,鼠标悬停到段落便可以显示解释,也可以选择fixed 模式,然后点击段落,便可以显示单词列表,可以通过设置style_element 的内容设置此脚本面板的样式 // @author lavaf // @match http://127.0.0.1:8848/TestyoudaoTranslate/pages/testyouhou.html // @match http://www.51voa.com/* // @match http://phantomjs.org/* // @match https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.clientsize?view=netframework-4.8 // @match http://localhost/pages/testyouhou.html // @match https://developer.android.google.cn/reference/android/support/design/widget/FloatingActionButton?hl=en // @grant none // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.js // @require https://cdn.jsdelivr.net/gh/emn178/js-sha256/build/sha256.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js // ==/UserScript== (function() { 'use strict'; var appKey = '28aabe54f8ad1add'; var Secret = 'Uf2mY5g5x6OyCIE0EPmDCJFilo4cWqvB'; //注意:暴露appSecret,有被盗用造成损失的风险 var from_l = 'en'; var to = 'zh-CHS'; var no_translate = ['a', 'an', 'the', 'than', 'would', 'rather', 'may', 'woman', 'women', 'man', 'other', 'any', 'many', 'some', 'have', 'has', 'for', 'on', 'to', 'of', 'in', 'from', 'first', 'second', 'am', 'is', 'are', 'were', 'be', 'was', 'will', 'did', 'do', 'does', 'no', 'not', 's', 'n', 'you', 'us', 'myself', 'herself', 'his', 'he', 'her', 'i', 'me', 'him', 'they', 'it', 'it\'s', 'so', 'or', 'and', 'where', 'there', 'this', 'that', 'what', 'done', 'with', 'at', 'she' ] var no_idom = ['of'] //不需要翻译短语 var textPanel; //全局的单词列表面板对象 var result_count = 0; //已经获得的单词的数量 var result_table = {} //全部已经获得的单词 var deleted_table = {} //已经删除不再显示的单词 var speech_table = {} //存放音标和发音 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('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 panel = createControlPanel(); var audio = $("<audio>", { src: "" }).appendTo("body"); var style_element = $('<style>').appendTo('head').text('#lavaf-start-get-word-button{color:red}'+ '#lavaf-control-panel{background-color:white;z-index:100}') // var script_element=$('<script>').appendTo('body').text('function lavaf_play(src) {if (src == undefined) {return;}var audio=$("audio");audio.attr(\'src\', src);audio.get(0).play()};'+ // "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));}") /** * 获取上一次选择的类型 */ function getLastSelectionType() { 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; } } return show_type_last_selection; } /** * 创建控制面板 */ function createControlPanel() { let div = $("<div>", { 'id': 'lavaf-control-panel' }).css({ 'position': 'absolute', 'left': '10px', 'top': '100px', 'border': '#000000 solid 1px', 'padding': '0 0 10px 0', 'z-index':100 }) let taskBarOfControlPanel=$('<button>').text("恢复").css({ 'display':'none', 'position':'absolute', 'bottom':'0', 'left':'0' }).appendTo('body').click(function(){ $(this).css('display','none') div.css('display','block') }) let controlHead=$('<div>',{ 'id':'lavaf-control-head' }).appendTo(div); var move_button = $("<button>", { 'id': "lavaf-move-button", 'title':'点击这里可以移动' }).text("+++++++++").css({ 'width':'80%' }).mousedown(function(e) { offestX = e.clientX - div.offset().left offestY = e.clientY - div.offset().top; movable = true; }).appendTo(controlHead); var minibButton=$('<button>',{ 'title':'最小化' }).text("-").css({ 'width':'15%' }).click(function(){ div.css('display','none') taskBarOfControlPanel.css('display','inline'); adw.show("看屏幕左下角") }); controlHead.append(minibButton) let controlBody=$("<div>",{ 'id':"lavaf-control-body" }).css({ 'padding':'10px', }).appendTo(div) appendMouseMoveEvents(div) /* 显示保存的单词数量 */ $("<div>", { 'id': 'lavaf-show-saved-word-num' }).text(`保存的单词:${result_count}`).appendTo(controlBody); //获取上次的选择 var show_type_last_selection = getLastSelectionType(); var selection_type_show = $("<select>", { id: "lavaf-selection-type-show" }).appendTo(controlBody); var option1 = $("<option>", { 'id': 'lavaf-selection-type-option-itme-1', 'class': 'lavaf-selection-type-option-item', value: "title" }).text("title"); var option2 = $("<option>", { 'id': 'lavaf-selection-type-option-itme-2', 'class': 'lavaf-selection-type-option-item', value: 'fixed' }).text("fixed"); if (show_type_last_selection === "title") { option1.attr('selected', "selected") } else { option2.attr('selected', "selected") } selection_type_show.append(option1).append(option2); var mutil_container = $("<div>", { id: 'lavaf-setting-multiple-select-container' }); var need_show_component = $("<select>", { 'id': 'lavaf-setting-multiple-select', 'multiple': 'multiple' }).change(function() { let child = need_show_component.children() if (child.get(3).selected) { child.each(function(index, element) { if (index !== 3) { element.selected = false; element.blur() for (let s of setting_item_name) { setting[s] = false; } } }) } else { child.each(function(index, element) { if (index != 3) { setting[setting_item_name[index]] = element.selected; } }) } localStorage.setItem('setting', JSON.stringify(setting)) }).appendTo(mutil_container); /* 为 select 标签添加数据 */ var component_array = ["显示删除单词按钮", "显示音标", "显示发音按钮", "无"] for (let item in component_array) { var option_show_delete_word = $("<option>", { id: 'lavaf-setting-multiple-select-option-' + item, class: 'lavaf-setting-multiple-select-option-item', value: component_array[item] }).text(component_array[item]).appendTo(need_show_component); if (item != 3) { var item_selected = setting[setting_item_name[item]] option_show_delete_word.attr('selected', item_selected) if (item_selected) { option_show_delete_word.get(0).focus() } else { // option_show_delete_word.blur } } } controlBody.append(mutil_container); //显示result-table 面板 var show_result_table_panel_button = $("<button>", { id: 'lavaf-show-result-table-panel-button' }).text("显示全部单词").appendTo(controlBody); show_result_table_panel_button.click(function() { let r = ""; for (let var1 in result_table) { if (deleted_table[var1] == null) r += getWordListItem(var1); } showTextPanel(r); }); controlBody.append('<br>') var show_deleted_word = $('<button>', { id: 'lavaf-show-deleted-word-button' }).text("显示已经删除的单词").appendTo(controlBody); show_deleted_word.click(function() { let r = ''; for (let var1 in deleted_table) { r += getWordListItem(var1); r += "<p>" + JSON.stringify(deleted_table[var1]) + "</p>"; } showTextPanel(r); }); controlBody.append('<br>') var start = getButton(1); controlBody.append(start); return div.appendTo('body'); } /** * 有道提供查词功能 * @param {string} 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.mousemove(function(e) { // var e = e || window.event; if (movable) { var move_x = e.clientX - offestX; var move_y = e.clientY - offestY; div.css({ 'top': move_y + "px", 'left': move_x + "px" }) } }).mouseup(function() { movable = false; }) } /** * 创建显示单词列表的面板 * @param {Object} str 要显示的html 内容 */ function createTextPanel(str) { var div = $("<div>").css({ 'position': 'absolute', 'top': (100 + 10) + "px", 'left': (100 + 10) + "px", 'background-color': 'lightgray', 'color': 'black', 'z-index':90 }); var inner_button = $("<button>").text("◍").css({ 'padding': '10px' }).appendTo(div); inner_button.mousedown(function(e) { offestX = e.clientX - div.offset().left; offestY = e.clientY - div.offset().top movable = true; }); appendMouseMoveEvents(div) var close_button = $("<button>", { id: 'text-panel-close-button' }).text("X").appendTo(div); close_button.click(function() { textPanel.css('display', 'none') }) var inner_dix = $("<div>").css('padding', '10px').html(str).appendTo(div); return div; } function addWin(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 = 'lavaf-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 {HtmlElement} current_element */ function addTitleOrText(word_table, current_element) { var current_selection_index = document.getElementById("lavaf-selection-type-show").selectedIndex if (current_selection_index == 0) { //显示title let result = ""; for (let var1 in word_table) { let word_name = word_table[var1]; if (result_table[word_table[var1]] != null) if (deleted_table[word_name] == null) result += word_table[var1] + ":" + result_table[word_table[var1]] + "\n"; } current_element.title = result; localStorage.setItem("save-show-type", "title") } else { localStorage.setItem("save-show-type", "fixed") current_element.onclick = function() { showTextPanel(getResult(word_table)) } // console.log('绑定事件到'); // console.log(current_element) } } function getResult(word_table) { let result = ''; for (let var1 in word_table) { //显示文本面板 let word_name = word_table[var1]; if (result_table[word_name] != null) { if (deleted_table[word_name] == null) result += getWordListItem(word_name); } } // showTextPanel(result) return result } /** * 显示单词列表框 * @param {Object} result */ function showTextPanel(result) { if (textPanel == null) { textPanel = createTextPanel(result); textPanel.appendTo($('body')) localStorage.setItem("save-show-type", "fixed") } else { //如果不为空就显示 var currentTextPanelStatus = textPanel.css('display'); textPanel.children().last().html(result) if (currentTextPanelStatus == 'none') { textPanel.css('display', 'block') } } $(".lavaf-button-play").click(function(){ lavaf_play($(this).attr("data-u")); }) $(".lavaf-button-delete").click(function(){ delete_word($(this).attr("data-d")) }) } /** * 将单词添加到已删除列表 * @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; var ph; var usph; var uk; var us; if (speech_table[word_name] != undefined) { ukph = speech_table[word_name]['uk-ph']; usph = speech_table[word_name]['us-ph'] ph = speech_table[word_name]['ph']; uk = speech_table[word_name]['uk'] us = speech_table[word_name]['us'] } else { console.log(word_name + " 这个可能不是单词,建议去除"); } 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 '<div><span style=\"color:red;\">' + word_name + "</span>" + (setting_1 ? '<span>【' + ph + '】</span>' : '') + (setting_1 ? '<span>[' + (ukph == undefined ? 'x' : ukph) + ']</span>' : '') + (setting_2 ? '<button class="lavaf-button-play" data-u=\"' + uk + '\"\'>o</button>' : '') + (setting_1 ? '<span>[' + (usph == undefined ? 'x' : usph) + ']</span>' : '') + (setting_2 ? '<button class="lavaf-button-play" data-u=\"' + us + '\")\'>o</button>' : '') + ":" + result_table[word_name] + (setting_0 ? '<button class="lavaf-button-delete" data-d=\"' + word_name + '\")\'>x</button>' : '') + "</div>"; } /** * 播放音频 * @param {Object} src 音频连接 */ function lavaf_play(src) { if (src == undefined) { addWin("当前单词没有发音,可能不是个单词"); return; } audio.attr('src', src) audio.get(0).play() } /** * 因为有的单词可能更快查找到,但是同一段的其他可能还没有 * 等到其他单词,也就是最后一个也查找到了, * 便可以给这个段落设置监听事件了 * ,查看当前需要索引的单词是否都已经查找到,如果是那就开始显示 * @param {Object} word_table 当前的标签含有的单词表 * @param {Object} current_element */ function addTitleForP(word_table, current_element) { // console.log(word_table); // console.log(word_table.length); // console.log(result_table); var m = 0; for (; 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.click(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 = current_element.innerText // 字符串 存储当前p 标签的内容 let p_inner = p_text.split(/[ \s,"'():.]/); //数组 存储当前p 标签的每一个单词 let word_table = [] //数组 存储需要索引的全部单词,这是需要翻译单词的 let last_word = null; for (var j = 0; j < p_inner.length; j++) { //遍历每一个单词 let item_query = p_inner[j]; /*去除非单词结果*/ let trim = item_query.trim(); if (trim === "" || trim === "-" || trim === '.' || !/^[0-9a-zA-Z-]{1,}$/.test(item_query)) { if (j == p_inner.length - 1) { addTitleForP(word_table, current_element); } continue; } /*去除非单词部分,保险措施*/ var last_char = item_query[item_query.length - 1]; if (last_char === ',' || last_char === '.' || last_char === '\'' || last_char === ')') { console.log('去除非单词部分' + item_query); item_query = item_query.substring(0, item_query.length - 1) console.log('处理之后' + item_query) } /*去除所有格*/ if (item_query.lastIndexOf("'s") >= 0) { item_query = item_query.substring(0, item_query.length - 2) } if (item_query[0] == '(') { item_query = item_query.substring(1, item_query.length - 1); } last_word = item_query.toLowerCase(); if ($.inArray(item_query.toLowerCase(), no_translate) == -1) { 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) { $.ajax({ url: location.protocol+'//openapi.youdao.com/api', type: 'post', dataType: 'jsonp', data: { q: item_query, appKey: appKey, salt: salt, 'from': from_l, to: to, curtime: curtime, sign: sign, signType: "v3" }, success: function(data) { 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_quer:translation == undefined:" + item_query); console.log(data); } } addTitleForP(word_table, current_element); } }); //查找调用完毕 } else { /* 找到单词不必联网获取 */ console.log("已获取" + item_query); // console.log(j+" "+p_inner.length); // console.log(p_inner); if (j == p_inner - 1) { addTitleForP(word_table, current_element); } } } //查找调用类型配置完毕,TODO:: 可以添加其他类型的查询结构 } //其余过于简单不必翻译, 翻译调用结束 //遍历单词结束 } //退出遍历单词循环 } //遍历段落结束 }) //监听函数设置完毕 } /** * 创建按钮 */ function getButton(type) { var btn_start = $("<button>", { id: 'lavaf-start-get-word-button', value: '开始', type: 'button' }).text('开始'); setOnClick(btn_start, type); return btn_start; } })();