Njord平台插件

Njord平台增强插件

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Njord平台插件
// @namespace    Njord Script
// @description  Njord平台增强插件
// @author       yhw
// @version      2.2.10
// @match        http://mark.meituan.com/*
// @match        https://mark.meituan.com/*
// @run-at       document-start
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @grant        GM_info
// @grant		     GM_getValue
// @grant		     GM_setValue
// @require      https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js
// @require      http://cdn.staticfile.org/TimeMe.js/2.0.0/timeme.min.js
// ==/UserScript==//

(function () {
    'use strict';

    const VERSION = GM_info.script.version; // 脚本@version
    const ADDRESS = 'http://ai.speechocean.com'; // 预识别服务器地址
    const WAVEITEMS = new Array(); // 音频信息集合
    const USER = {}; // 用户信息
    const PROJECT = {}; // 项目信息
    const queryTimeOut = 1000; // 长音频轮询间隔
    var APIKEY = ""; // 预识别apikey
    var Type = 0;
    var IsStopObserver = false;

    const log_body = {
        project_id: -1, // 当前的项目id
        user_id: -1, // 当前用户id
        user_group: "0", // 客户平台的用户所属供应商标识,需要与pm沟通确认
        role: "0", // 当前用户的角色
        user_action: "", // 用户做的操作
        others: ""
    }
    const send_log = (log_body) => {
        log_body.crx_code = "b556d7bb-ce3e-4d8b-9f17-a1a777b8308b"; // 插件代号,每个插件代号唯一
        log_body.crx_platform = "Meituan-Njord" + "-" + APIKEY; // 插件平台,插件应用的客户平台
        const timestamp = Date.parse(new Date());
        //console.log(timestamp + log_body);
        const signature = md5(`unQaoqsPZhSuX5ni${timestamp}`);
        sendLog(signature, {
            ...log_body,
            timestamp: timestamp
        });
    }

    function appendInput() {
        const style = ".__j-container { top: 15%; font-size: 12px; right: 0px; background:violet; padding:5px; position: fixed; z-index: 100000; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none}.__j-input { width: 100px; }";
        var html =
            '<div class="__j-container">' +
            '<div>\n' +
            '    <label class="__j-label">语言设置</label>\n' +
            '    <select class="__j-input" id="__j-language">\n' +
            '        <option>普通话</option>\n' +
            '        <option>粤语(中国广州)</option>\n' +
            '        <option>广东粤语</option>\n' +
            '        <option>粤语(中国香港)</option>\n' +
            '    </select>\n' +
            '</div>' +
            '<div>\n' +
            '    <label class="__j-label">API KEY</label>\n' +
            '    <input class="__j-input" id="__j-apikey" type="text" name="APIKEY"></input>\n' +
            '</div>' +
            '</div>'
        '';
        var stylenode = document.createElement('style');
        stylenode.setAttribute("type", "text/css");
        if (stylenode.styleSheet) {// IE
            stylenode.styleSheet.cssText = style;
        } else {// w3c
            var cssText = document.createTextNode(style);
            stylenode.appendChild(cssText);
        }
        var node = document.createElement('div');
        node.innerHTML = html;
        document.head.appendChild(stylenode);
        document.body.appendChild(node);
        Type = GM_getValue(GM_info.script.name + "-Type", "");
        document.getElementById("__j-language").options.selectedIndex = Type;
        document.getElementById("__j-language").onchange = (e) => {
            Type = e.srcElement.selectedIndex;
            GM_setValue(GM_info.script.name + "-Type", Type);
        };
        APIKEY = GM_getValue(GM_info.script.name + "-APIKEY", "");
        document.getElementById("__j-apikey").value = APIKEY;
        document.getElementById("__j-apikey").onchange = (e) => {
            APIKEY = e.srcElement.value;
            GM_setValue(GM_info.script.name + "-APIKEY", APIKEY);
        };
    }
    // appendInput();

    // 页面元素监控,如果添加片段,重新进行文本检查
    var observer = new MutationObserver(function (mutations, observer1) {
        if (IsStopObserver) return;
        console.log('-------------------事件触发-----------------------');
        queryCheck();
        addElement();
    });

    /**
     * 重载fetch,用于拦截网页发送的fetch请求
     */
    let originFetch = fetch;
    window.unsafeWindow.fetch = async function (...args) {
        const response = await originFetch(...args);
        if (USER["info"] == null && localStorage["user"] != null) {
            USER = JSON.parse(localStorage["user"]);
        }
        if (PROJECT["info"] == null && localStorage["project"] != null) {
            PROJECT = JSON.parse(localStorage["project"]);
        }
       if(args[0].url.startsWith('https://mark.meituan.com/process/file/proxy')) {
            /*observer.observe(getObserverElement(), {
                childList: true,
                subtree: true,
            });
            addTextElement("加载中...");
            let url = args[0].url;
            //let waveInfo = Object.fromEntries(new URLSearchParams(url.substring(url.indexOf('?'))).entries());
            let waveURL = decodeURIComponent(url.substring(url.indexOf("jump=") + 5));
            // 开启页面有效停留计时器
            TimeMe.resetRecordedPageTime(waveURL);
            TimeMe.initialize({
                currentPageName: waveURL,
                idleTimeoutInSeconds: 10 // seconds
            });*/
            queryCheck();
            addElement();
       }
       return response;
    }

    /**
     * 重载xhr的send,用于拦截网页发送的xhr请求
     */
    let xhr = XMLHttpRequest.prototype;
    let open = xhr.open;
    let send = xhr.send;
    xhr.open = function (method, url) {
        this._method = method;
        this._url = url;
        return open.apply(this, arguments);
    };

    xhr.send = async function (postData) {
        if (this._method.toLowerCase() === 'post') {
            this.addEventListener('load', function () { // 获取响应数据
                /// 用户信息
                if (this._url.indexOf('/process/user/profile') > -1) {
                //if (this._url.startsWith('/process/user/profile')) {
                    console.log('--------用户信息');
                    let user = JSON.parse(this.response)['data'];
                    USER["valid"] = user['departmentName'] == "HT";
                    USER["info"] = user;
                    localStorage["user"] = JSON.stringify(USER);
                }
                /// 选择项目
                if (this._url.indexOf('/process/acl/switchproject') > -1) {
                //if (this._url.startsWith('/process/acl/switchproject')) {
                    console.log('--------选择项目');
                    let project = JSON.parse(this.response)['data'];
                    PROJECT["info"] = project;
                    localStorage["project"] = JSON.stringify(PROJECT);
                }
            });
            // if (this._url.startsWith('/process/assist/autoquality')) {
            // 	console.log('--------提交检查');
            // 	let vue = getVueInstance();
            // 	if (vue != null) {
            // 		var time = TimeMe.getTimeOnPageInMilliseconds(vue["sourceData"]["content"]);
            // 		TimeMe.stopTimer();
            // 		let data = getSubmitData(postData, time);
            // 		let ret = await postResult({ address: ADDRESS, authKey: APIKEY, version: VERSION, data: data });
            // 		log_body.others = JSON.stringify(data);
            // 		log_body.user_action = "SubmitCheck";
            // 		send_log(log_body);
            // 		console.log(ret);
            // 	}
            // }
            /// 提交质检数据(只取提交的请求信息)
            if (this._url.indexOf('/process/annotate/updatetask') > -1) {
            // if (this._url.startsWith('/process/annotate/updatetask')) {
                console.log('--------提交数据');
                /*let vue = getVueInstance();
                if (vue != null) {
                    var time = TimeMe.getTimeOnPageInMilliseconds(vue["sourceData"]["content"]);
                    TimeMe.stopTimer();
                    let data = getSubmitData(postData, time);
                    let ret = await postResult({ address: ADDRESS, authKey: APIKEY, version: VERSION, data: data });
                    log_body.others = JSON.stringify(data);
                    log_body.user_action = "Submit";
                    send_log(log_body);
                    console.log(ret);
                }*/
            }
        }
        return send.apply(this, arguments);
    };

    function getLastWaveItem(waveURL) {
        let items = WAVEITEMS.filter(v => v['waveSource'] == waveURL);
        return items.length > 0 ? items[items.length - 1] : null;
    }

    function getSubmitData(postData, time) {
        let vue = getVueInstance();
        if (vue != null) {
            let submit = Object.fromEntries(new URLSearchParams(postData).entries());
            submit['sourceData'] = vue['sourceData'];
            submit['meta'] = JSON.parse(submit['meta']);
            submit['meta']['data']['markarea'] = submit['meta']['data']['markarea'] != undefined ? JSON.parse(submit['meta']['data']['markarea']) : [];
            let isQaData = submit['sourceData']['data']['recordId'] != null; // 有recordId的是质检数据,否则是标注数据
            let waveURL = submit['meta']['content'];
            let waveItem = getLastWaveItem(waveURL);
            if (waveItem != null) {
                let taskid = waveItem['waveTaskId'];
                let json = {
                    project_name: PROJECT["info"]['projectName'] + "[" + submit['annLogId'] + "]",
                    task_id: taskid,
                    info: {
                        provider: "meituan",
                        name: "Njord",
                        time: time,
                        audioDuration: submit['sourceData']['data']['duration_ms'],
                        label: !isQaData,
                        valid: submit['meta']['data']['has_invalid'] == "1",
                        text: submit['meta']['data']['custom'],
                        user: USER["info"] == null ? null : USER["info"]['userName'],
                        row: submit,
                        userFull: USER,
                    },
                    data: submit['meta']['data']['markarea'].map(item => {
                        return {
                            min: item['area'].split(',')[0],
                            max: item['area'].split(',')[1],
                            text: item['content'],
                            noise: item['has-noise'],
                            accent: item['has-accent'],
                            sex: item['sex'],
                        };
                    }),
                    origin: waveItem["origin"]["data"],
                    full: waveItem,
                    version: VERSION
                };
                return json;
            }

        }
    }

    function getVueInstance() {
        var elements = document.getElementsByClassName("now-wrap");
        if (elements.length > 0) {
            return elements[0].__vue__;
        }
        return null;
    }

    /**
     * 获取音频预识别id
     * @param {{
     * address:String;
     * authKey:String;
     * bolbUrl:String;
     * language:String;
     * type:Number;
     * normal:Boolean;
     * version:String;
     * }} waveInfo 音频信息
     */
    function getWaveId(waveInfo) {
        return new Promise((resolve, reject) => {
            try {
                if (waveInfo.type == 2) {
                    GM_xmlhttpRequest({
                        url: `${waveInfo.address}/speech/api/v1/asr/create_task`,
                        method: "POST",
                        headers: {
                            "Authorization": "Bearer " + waveInfo.authKey,
                            "Version": waveInfo.version
                        },
                        onreadystatechange: function (responseDetails) {
                            if (responseDetails.readyState === 4) {
                                let d = JSON.parse(responseDetails.response);
                                d.data = { "task_id": d.task_id };
                                resolve(d);
                            }
                        }
                    });
                }
                else if (waveInfo.type == 1) {
                    getLongAudioId(waveInfo).then(v => resolve(v));
                }
                else {
                    reject(`audio type error ${waveInfo.type}`);
                }
            } catch (ex) {
                reject(ex);
            }
        });
    }

    function getLongAudioId(waveInfo) {
        return new Promise((reslove, reject) => {
            fetch(waveInfo.bolbUrl)
                .then(response => response.blob())
                .then(blob => {
                    let formdata = new FormData();
                    formdata.append("domain", waveInfo.language);
                    formdata.append("file_path", blob, "proxy.wav");
                    let headerdata = {
                        "Authorization": "Bearer " + waveInfo.authKey,
                        "Version": waveInfo.version,
                    };
                    if (waveInfo.normal == true)
                        headerdata["Knative-Serving-Tag"] = "normal";
                    GM_xmlhttpRequest({
                        url: `${waveInfo.address}/speech/api/v1/asr/task/create`,
                        method: "POST",
                        headers: headerdata,
                        data: formdata,
                        onreadystatechange: function (responseDetails) {
                            if (responseDetails.readyState === 4) {
                                reslove(JSON.parse(responseDetails.response));
                            }
                        }
                    });
                }).catch(e => reject(e));
        });
    }

    function queryWaveId(waveInfo, taskid) {
        return new Promise((reslove, reject) => {
            fetch(waveInfo.bolbUrl)
                .then(response => response.blob())
                .then(blob => {
                    let headerdata = {
                        "Authorization": "Bearer " + waveInfo.authKey,
                        "Version": waveInfo.version,
                    };

                    if (waveInfo.normal == true)
                        headerdata["Knative-Serving-Tag"] = "normal";
                    if (waveInfo.type == 2) { // 短音频
                        let formdata = new FormData();
                        formdata.append("domain", waveInfo.language);
                        formdata.append("wav_path", blob, "proxy.wav");
                        formdata.append("task_id", taskid)
                        GM_xmlhttpRequest({
                            url: `${waveInfo.address}/speech/api/v2/asr/recognize`,
                            method: "POST",
                            headers: headerdata,
                            data: formdata,
                            onreadystatechange: function (responseDetails) {
                                if (responseDetails.readyState === 4) {
                                    reslove(JSON.parse(responseDetails.response));
                                }
                            }
                        });
                    }
                    else if (waveInfo.type == 1) { // 长音频
                        queryTaskId(waveInfo, taskid, reslove);
                    } else {
                        reject(`audio type error ${waveInfo.type}`);
                    }
                }).catch(e => reject(e));

        });
    }

    function queryTaskId(waveInfo, taskId, reslove) {
        let ids = new Array();
        ids.push(taskId);
        GM_xmlhttpRequest({
            url: `${waveInfo.address}/speech/api/v1/asr/task/query`,
            method: "POST",
            headers: {
                "Authorization": "Bearer " + waveInfo.authKey,
                "Version": waveInfo.version,
                "Content-Type": "application/json"
            },
            data: JSON.stringify({
                task_ids: ids
            }),
            onreadystatechange: function (responseDetails) {
                if (responseDetails.readyState === 4) {
                    let queryResponse = JSON.parse(responseDetails.response);
                    if (queryResponse["data"][0]["task_status"].toLowerCase() == "succeed") {
                        queryResponse["data"] = queryResponse["data"][0];
                        reslove(queryResponse);
                    }
                    else {
                        // 根据指定的时间间隔轮询
                        setTimeout(queryTaskId, waveInfo.queryInterval, waveInfo, taskId, reslove);
                    }
                }
            }
        });
    }

    /**
     * 提交音频进行预识别
     * @param {{
     * address:String;
     * authKey:String;
     * bolbUrl:String;
     * type:Number;
     * normal:Boolean;
     * queryInterval:Number;
     * version:String;
     * }} waveInfo 音频信息
     * @returns {Promise} aa
     */
    function postWave(waveInfo) {
        return new Promise((reslove, reject) => {
            fetch(waveInfo.bolbUrl)
                .then(response => response.blob())
                .then(blob => {
                    let headerdata = {
                        "Authorization": "Bearer " + waveInfo.authKey,
                        "Version": waveInfo.version,
                    };
                    if (waveInfo.normal == true)
                        headerdata["Knative-Serving-Tag"] = "normal";
                    if (waveInfo.type == 2) { // 短音频
                        let formdata = new FormData();
                        formdata.append("domain", "zh_cn");
                        formdata.append("wav_path", blob, "proxy.wav");
                        GM_xmlhttpRequest({
                            url: `${waveInfo.address}/speech/api/v2/asr/recognize`,
                            method: "POST",
                            headers: headerdata,
                            data: formdata,
                            onreadystatechange: function (responseDetails) {
                                if (responseDetails.readyState === 4) {
                                    reslove(JSON.parse(responseDetails.response));
                                }
                            }
                        });
                    }
                    else if (waveInfo.type == 1) { // 长音频
                        let formdata = new FormData();
                        formdata.append("domain", "zh_cn");
                        formdata.append("file_path", blob, "proxy.wav");
                        GM_xmlhttpRequest({
                            url: `${waveInfo.address}/speech/api/v1/asr/task/create`,
                            method: "POST",
                            headers: headerdata,
                            data: formdata,
                            onreadystatechange: function (responseDetails) {
                                if (responseDetails.readyState === 4) {
                                    let createResponse = JSON.parse(responseDetails.response);
                                    queryTaskId(waveInfo, createResponse["data"]["task_id"], reslove);
                                }
                            }
                        });
                    } else {
                        reject(`audio type error ${waveInfo.type}`);
                    }
                }).catch(e => reject(e));

        });
    }

    /**
     * 提交数据到后端服务
     * @param {{
     * address:String;
     * authKey:String;
     * version:String;
     * data:object;
     * }} postData
     * @param {*} callback
     */
    function postResult(postData) {
        return new Promise((reslove, reject) => {
            try {
                GM_xmlhttpRequest({
                    url: `${postData.address}/speech/api/v1/asr/label_result`,
                    method: "POST",
                    headers: {
                        "Authorization": "Bearer " + postData.authKey,
                        "Version": postData.version,
                        "Content-Type": "application/json"
                    },
                    data: JSON.stringify(postData.data),
                    onreadystatechange: function (responseDetails) {
                        if (responseDetails.readyState === 4) {
                            reslove(JSON.parse(responseDetails.response));
                        }
                    }
                });
            }
            catch (e) {
                reject(e);
            }
        })
    }

    function autoSplit(vueInstance, data) {
        if (vueInstance != null) {
            let userData = vueInstance['userData'];
            /// 如果没有标注结果,自动填充预识别结果
            if (userData['markarea'] == null && userData["recordId"] == null) {
                if ((data.length > 1 || data[0].text != "")) {
                    let markarea = new Array();
                    let reg6 = new RegExp("[。?!]$", "g");
                    for (let i = 0; i < data.length; i++) {
                        let start = data[i]['start'];
                        start = start == 0 ? 0.05 : start;
                        let end = data[i]['end'];
                        let text = data[i]['text'];
                        text = reg6.test(text) ? text : text.replace(/[,.?!,]+$/g, "") + "。";
                        markarea.push({
                            area: start + "," + end,
                            'has-noise': "无",
                            'has-accent': "无",
                            sex: "男",
                            content: text
                        });
                    }
                    IsStopObserver = true;
                    try {
                        userData['has_invalid'] = 1;
                        userData['markarea'] = JSON.stringify(markarea);
                        vueInstance.updateValue();
                    } finally {
                        IsStopObserver = false;
                    }
                }
            }
        }
    }

    function addTextElement(text) {
        var labels = document.getElementsByClassName('source-content');
        if (labels.length > 0) {
            let label = labels[0];
            if (label.childElementCount > 0) {
                var audioSource = label.children[0];
                if (audioSource.childElementCount > 0) {
                    if (audioSource.__SPOC__ == null) {
                        let div = document.createElement("div");
                        div.className = "flex";
                        let span = document.createElement("span");
                        span.id = "SPOC"
                        let name = document.createElement("div");
                        name.innerText = "参考内容";
                        name.style["left"] = "-100px";
                        name.style["font-size"] = "10px";
                        name.style["z-index"] = "1000";
                        name.style["color"] = "#409eff";
                        name.style["margin-top"] = "3px";
                        name.style["position"] = "absolute";
                        div.appendChild(span);
                        div.appendChild(name);
                        audioSource.appendChild(div);
                        audioSource.__SPOC__ = true;
                    }
                    document.getElementById("SPOC").innerText = text;
                }
            }
        }
    }

    function getObserverElement() {
        // var forms = document.getElementsByTagName("form");
        // for (let i = 0; i < forms.length; i++) {
        //     let form = forms[i];
        //     if (form.innerText.startsWith("音频截取")) {
        //         return form;
        //     }
        // }
        // return null;
        return document.getElementsByClassName('el-main')[0];
    }

    function queryCheck() {
        // if (USER["valid"] != true) return;
        observer.disconnect();
        try {
            const elementMain = document.getElementsByClassName('el-main')[0];
            var buttons = elementMain.getElementsByClassName('el-button--default');
            var buttonsCount = buttons.length;
            for (var i = 0; i < buttonsCount; i++) {
                var button = buttons[i];
                var spans = button.getElementsByTagName('span');
                if (spans != null && spans.length == 1) {
                    if (spans[0].innerText.startsWith('删除("')) {
                        buttonCheck(buttons[i]);
                    }
                }
            }
            var textareas = elementMain.getElementsByClassName('el-textarea__inner');
            var textareasCount = textareas.length;
            for (var i = 0; i < textareasCount; i++) {
                var textarea = textareas[i];
                if (textarea.__SPOC__ == null) {
                    textarea.__SPOC__ = textareaCheck;
                    if (textarea.onkeyup == null) {
                        textarea.onkeyup = function () {
                            this.__SPOC__(this);
                        }
                    }
                }
                textarea.__SPOC__(textarea);
            }
        } finally {
            observer.observe(getObserverElement(), {
                childList: true,
                subtree: true,
            });
        }
    }


    function addElement() {
        if (USER["valid"] != true) return;
        observer.disconnect();
        try {
            let ds = document.getElementsByClassName('el-pagination');
            if (ds.length == 0) return;
            let d = ds[0].parentElement;
            let divCount = d.childElementCount;
            for (let i = 1; i < divCount; i++) {
                let item = d.children[i];
                if (item.children[0].childElementCount == 3) {
                    let tmp = document.createElement('div');
                    tmp.innerHTML = '<button type="button" class="el-button el-button--default" style="margin-left: 10px;"><span>插入{}</span></button>'
                        + '<button type="button" class="el-button el-button--default" style="margin-left: 10px;"><span>插入{er}</span></button>';
                    var buttonCount = tmp.childElementCount;
                    for (let j = 0; j < buttonCount; j++) {
                        let button = tmp.children[0]; // 被使用的button会从tmp中移除,每次都取第一个即可
                        button.onclick = function () {
                            var textarea = this.parentElement.parentElement.getElementsByTagName('textarea')[0];
                            let startPos = textarea.selectionStart; // 获取光标开始的位置
                            let endPos = textarea.selectionEnd; // 获取光标结束的位置
                            if (startPos === undefined || endPos === undefined) return; // 如果没有光标位置 不操作
                            let oldText = textarea.value; // 获取输入框的文本内容
                            let insertText = this.innerText.match(new RegExp("{[^{}]*}", "g"))[0];
                            let newText = oldText.substring(0, startPos) + insertText + oldText.substring(endPos); // 将文本插入
                            textarea.value = newText; // 将拼接好的文本设置为输入框的值
                            textarea.focus(); // 重新聚焦输入框
                            textarea.selectionStart = startPos + (insertText.length == 2 ? 1 : insertText.length);
                            textarea.selectionEnd = startPos + (insertText.length == 2 ? 1 : insertText.length);
                            textarea.dispatchEvent(new Event('input')); // 触发input事件
                            textareaCheck(textarea);
                        };
                        item.children[0].append(button);
                    };
                }
            }
        } finally {
            observer.observe(getObserverElement(), {
                childList: true,
                subtree: true,
            });
        }
    }




    function textareaCheck(textarea) {
        let target = textarea;
        let results = checkText(target.value);
        let errors = results['errors'];
        let warns = results['warns'];
        // 先清理掉之前的错误提示
        target.style.borderColor = null;
        let parent = target.parentNode;
        if (parent != null) {
            let span = parent.getElementsByClassName('error-tip')[0];
            if (span != undefined) {
                target.parentNode.removeChild(span);
            }
            // 如果有错误,则添加错误+警告
            if (errors.length > 0) {
                target.style.borderColor = 'red';
                let span = document.createElement('span');
                span.className = "error-tip";
                span.innerText = errors.concat(warns).join(',');
                span.style.color = 'red';
                parent.appendChild(span);
            } else if (warns.length > 0) {
                target.style.borderColor = 'orange';
                let span = document.createElement('span');
                span.className = "error-tip";
                span.innerText = warns.join(',');
                span.style.color = 'orange';
                parent.appendChild(span);
            }
        }
    }

    function buttonCheck(button) {
        var target = button;
        var text = button.getElementsByTagName("span")[0].innerText;
        var hasError = checkTime(text);
        // 先清理掉之前的错误提示
        target.style.borderColor = null;
        var parent = target.parentNode;
        if (parent != null) {
            var span = parent.getElementsByClassName('error-tip')[0];
            if (span != undefined) {
                target.parentNode.removeChild(span);
            }
            // 如果有错误,则添加错误提示
            if (!hasError) {
                target.style.borderColor = 'red';
                var span = document.createElement('span');
                span.className = "error-tip";
                span.innerText = '时长错误';
                span.style.color = 'red';
                parent.appendChild(span);
            }
        }
    }


    function checkTime(text) {
        var timeStr = text.split('"')[1];
        var time = timeStr.split(',');
        var min = parseFloat(time[0]);
        var max = parseFloat(time[1]);
        return (max - min) <= 20;
    }

    function checkText(text) {
        //console.info('文本检查:', text)
        let errors = new Array();
        let warns = new Array();
        if (text == undefined) return { errors: errors, warns: warns };
        // 空文本
        if (text.length == 0) {
            errors.push('文本为空');
            return { errors: errors, warns: warns };
        }
        let regEnglish = new RegExp("([A-Za-z]+['-]+[A-Za-z]+)|([A-Za-z]+)", "g");
        /// 一个字符或者一个英文单词
        if (text.length == 1 || text.match(regEnglish) == text) {
            errors.push('字数过少');
            return { errors: errors, warns: warns };
        }
        let letterText = text.replace(/[\ |\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?|\、|\,|\;|\。|\?|\!|\“|\”|\‘|\’|\:|\(|\)|\─|\…|\—|\·|\《|\》]/g, "");
        let regUseless = new RegExp("[嗯啊呀哇哦啦呢咚呐吧吗呵呃唔嘛唉哎诶]+", "g");
        if (letterText.match(regUseless) == letterText || letterText.match(/(.)\1+/g) == letterText) {
            errors.push('全部语气词或重复');
            return { errors: errors, warns: warns };
        }
        // 判断标签是否正确
        let regTag = new RegExp("{[^{}]*}", "g");
        let tags = text.match(regTag);
        if (tags != null) {
            tags.forEach(function (item) {
                if (!checkTag(item))
                    errors.push("标签错误" + item);
            })
        }
        let noTagText = text.replace(regTag, ''); // 无标签文本
        // 有数字
        let reg1 = new RegExp("[0-9]+", "g");
        if (noTagText.match(reg1) != null) {
            errors.push("有数字");
        }
        // 英文首字母大写
        let reg11Match = noTagText.match(regEnglish);
        if (reg11Match != null) {
            let reg111 = new RegExp("^[^A-Z]", "g");
            let enErr = false;
            reg11Match.forEach(function (item) {
                if (item.match(reg111) != null)
                    enErr = true;
            })
            if (enErr)
                errors.push("英文首字母非大写");
        }
        // 英文和英文间有标点
        let reg13 = new RegExp("[a-zA-Z][,.!.,。?!]+[a-zA-Z]", "g");
        if (noTagText.match(reg13) != null) {
            errors.push("英文和英文间有标点");
        }

        // 英文连续大写错误
        let reg12 = new RegExp("[A-Z]{2,}", "g");
        if (noTagText.match(reg12) != null) {
            errors.push("连续大写错误");
        }

        let reg3 = new RegExp("[A-Za-z]+\\s+[\u4e00-\u9fa5]+", "g");
        if (text.match(reg3) != null) {
            errors.push("英文和中文有空格");
        }
        let reg4 = new RegExp("[\u4e00-\u9fa5]+\\s+[A-Za-z]+", "g");
        if (text.match(reg4) != null) {
            errors.push("中文和英文有空格");
        }
        let reg41 = new RegExp("[\u4e00-\u9fa5]+\\s+[\u4e00-\u9fa5]+", "g");
        if (text.match(reg41) != null) {
            errors.push("中文和中文有空格");
        }
        let reg5 = new RegExp("^\\s+", "g");
        if (text.match(reg5) != null) {
            errors.push("句头多余空格");
        }
        let reg5a = new RegExp("\\s{2,}", "g");
        if (text.trim().match(reg5a) != null) {
            errors.push("句中有多余空格");
        }
        let reg5b = new RegExp("\\s$", "g");
        if (text.match(reg5b) != null) {
            errors.push("句尾多余空格");
        }
        let reg6 = new RegExp("[。?!]$", "g");
        if (text.trim().match(reg6) == null) {
            errors.push("句尾标点错误");
        }
        let reg72 = new RegExp("( [,。?!])|([,。?!] )", "g");
        if (noTagText.match(reg72) != null) {
            errors.push("标点前后后空格");
        }
        let reg71 = new RegExp("[,。?!]{2,}", "g");
        if (noTagText.match(reg71) != null) {
            errors.push("有连续标点");
        }
        let reg7 = new RegExp("[^\u4e00-\u9fa5,。?!\\s\\w\\d]", "g");
        if (noTagText.match(reg7) != null) {
            errors.push("有非中文标点");
        }
        let reg8 = noTagText.match(/[^0-9\W]+|[\u4e00-\u9fa5]/g);
        if (reg8 != null && reg8.length == 1) {
            errors.push("只有一个字或单词");
        }
        if (noTagText.indexOf('儿') >= 0)
            warns.push('儿化音需输入{er},请确认"儿"书写正确');

        return { errors: errors, warns: warns };
    }

    function checkTag(text) {
        text = text.substring(1, text.length - 1);
        var reg = new RegExp("^[a-z]+[0-9]*", "g");
        return text.match(reg) == text;
    }

})();