Greasy Fork

Greasy Fork is available in English.

英语阅读助手

点击开始,获取p 标签的单词,然后获取解释,鼠标悬停到段落便可以显示解释,也可以选择fixed 模式,然后点击段落,便可以显示单词列表,可以通过设置style_element 的内容设置此脚本面板的样式

目前为 2019-06-20 提交的版本,查看 最新版本

// ==UserScript==
// @name         英语阅读助手
// @namespace    http://tampermonkey.net/
// @version      0.5
// @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/*
// @grant        none
// @license      Apache License 2.0
// @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 = 'h8mPIvs9kMEOUJG7bN1wYz1Da59shwg4'; //注意:暴露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 = {} 	//存放jsonp 的回调函数
    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}')

    /**
			 * 获取上一次选择的类型
			 */
    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': '10px'
        })
        var move_button = $("<button>", {
            'id': "lavaf-move-button"
        }).text("◉").css({
            'margin-right': '10px'
        }).mousedown(function(e) {
            offestX = e.clientX - div.offset().left
            offestY = e.clientY - div.offset().top;
            movable = true;
        }).appendTo(div)
        appendMouseMoveEvents(div)
        /*
				显示保存的单词数量
				*/
        $("<div>", {
            'id': 'lavaf-show-saved-word-num'
        }).text(`保存的单词:${result_count}`).appendTo(div);
        //获取上次的选择
        var show_type_last_selection = getLastSelectionType();
        var selection_type_show = $("<select>", {
            id: "lavaf-selection-type-show"
        }).appendTo(div);
        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
                }
            }
        }
        div.append(mutil_container);
        //显示result-table 面板
        var show_result_table_panel_button = $("<button>", {
            id: 'lavaf-show-result-table-panel-button'
        }).text("显示全部单词").appendTo(div);
        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);
        });
        div.append('<br>')
        var show_deleted_word = $('<button>', {
            id: 'lavaf-show-deleted-word-button'
        }).text("显示已经删除的单词").appendTo(div);
        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);
        });
        div.append('<br>')
        var start = getButton(1);
        div.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'
        });
        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 {

            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')
            }

        }

    }
    /**
			 * 将单词添加到已删除列表
			 * @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 onclick=\'play(\"' + uk + '\")\'>o</button>' : '') +
            (setting_1 ? '<span>[' + (usph == undefined ? 'x' : usph) + ']</span>' : '') +
            (setting_2 ? '<button onclick=\'play(\"' + 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) {
        // 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 = [] //数组 存储需要索引的全部单词
                for (var j = 0; j < p_inner.length; j++) { //遍历每一个单词
                    let item_query = p_inner[j];
                    /*去除非单词结果*/
                    let trim=item_query.trim();
                    if (trim === "" || trim === "-"||trim==='.') {
                        if(j==p_inner.length-1){
                            addTitleForP(word_table, current_element);
                        }
                        continue;
                    }
                    if(!/^[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);
                    }
                    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: 'http://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;
    }
})();