// ==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/[email protected]/dist/js/bootstrap.min.js
// ==/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 += "<p>" + JSON.stringify(deleted_table[var1]) + "</p>";
}
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 '<div><span style=\"color:red;\">' + word_name + "</span>" +
(setting_1 ? '<span>【' + speech_table[word_name]['ph'] + '】</span>' : '') +
(setting_1 ? '<span>[' + (ukph == undefined ? 'x' : ukph) + ']</span>' : '') +
(setting_2 ? '<button onclick=\'play(\"' + speech_table[word_name]['uk'] + '\")\'>o</button>' : '') +
(setting_1 ? '<span>[' + (usph == undefined ? 'x' : usph) + ']</span>' : '') +
(setting_2 ? '<button onclick=\'play(\"' + speech_table[word_name]['us'] + '\")\'>o</button>' : '') +
":" + result_table[word_name] +
(setting_0 ? '<button onclick=\'delete_word(\"' + word_name + '\")\'>x</button>' : '') +
"</div>";
}
/**
* 播放音频
* @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;
}
})();