鼠标拖拽

鼠标拖拽,按住鼠标左键拖拽选中文本、链接、图片后复制、打开、搜索,Alt+Y进入设置,Esc退出设置

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         鼠标拖拽
// @version      1.25
// @namespace    http://tampermonkey.net/
// @description  鼠标拖拽,按住鼠标左键拖拽选中文本、链接、图片后复制、打开、搜索,Alt+Y进入设置,Esc退出设置
// @author       lyscop
// @icon         https://i.imgur.com/obQUjIi.png
// @license             GNU General Public License v3.0 or later
// @include      *
// @run-at       document-end
// @grant        GM_addStyle
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM_setClipboard
// @grant        GM_download
// @grant        GM_addValueChangeListener
// @grant	     GM_registerMenuCommand
// @grant        GM_notification
// @grant        window.close
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @grant        GM_deleteValue
// @grant        GM_listValues
// @connect      dict.youdao.com
// @connect      translate.google.cn
// ==/UserScript==


(function() {
    'use strict';
    //==========①=========================
    let storage = {
        get: function(name, defaultValue) {
            return GM_getValue(name, defaultValue);
        },
        set: function(name, data) {
            return GM_setValue(name, data);
        }
    },
        runtime = {
            sendMessage: function(data){
                return Promise.resolve(this.processMessage(data));
            },
            processMessage: function(data){
                switch (data.subject) {
                    case "gestureFrameMousedown":
                    case "gestureFrameMousemove":
                    case "gestureFrameMouseup":
                        gestureHandler.handleMessage(data);
                        break;
                    case 'gestureChange':
                        /*if(this.captureGesture){
                            Ui.captureGesture(data.data.gesture, "recorddingGesture");

                            return;
                        }*/
                        try {
                            let actionName = '';
                            if(cfg.gesture[data.data.gesture].alias)
                                actionName = cfg.gesture[data.data.gesture].alias;
                            else
                                actionName = local.gesture[cfg.gesture[data.data.gesture].name][cfg.language];
                            return {action:actionName};
                        } catch(e) {}
                        break;
                    case 'gestureEnd':
                        /*if(this.captureGesture){
                            Ui.captureGesture(data.data.gesture);
                            return;
                        }*/
                        try {
                            let action = cfg.gesture[data.data.gesture];
                            Fn[action.name](action.arg, data.data);
                        } catch(e) {
                            // console.log(e);
                        }
                        break;
                    case 'dragChange':
                        if(this.captureGesture){
                            Ui.captureGesture(data.data.gesture, "recorddingGesture");

                            return;
                        }
                        try {
                            let actionName = '',
                                typeAndData = getDragFn(data.data);
                            if(typeAndData[1].alias)
                                actionName = typeAndData[1].alias;
                            else
                                actionName = local[typeAndData[0]][typeAndData[1].name][cfg.language];
                            return {action:actionName};
                        } catch(e) {}
                        break;
                    case 'dragEnd':
                        if(this.captureGesture){
                            Ui.captureGesture(data.data.gesture);
                            return;
                        }
                        try {
                            let action = getDragFn(data.data)[1];
                            Fn[action.name](action.arg, data.data);
                        } catch(e) {
                            // console.log(e);
                        }
                        break;
                    default:
                        break;
                }
            },
            captureGesture:false
        },
        _cfg = {
            Gesture: {
                mouseButton: 2,
                suppressionKey: "",
                distanceThreshold: 2,
                distanceSensitivity: 20,
                Timeout: {
                    active: true,
                    duration: 1
                }
            },
            Hinter: {
                background : '',
                fontSize: 0,
                lineColor: null,
                minLineWidth: 1,
                maxLineWidth: 10,
                lineGrowth: 0.6,
                funNotDefine: null
            },
            Drag: {
                linktextAslink: true,
                dragInTextarea: true
            },
            directions: 8,//方向
            language: "zh",
            gesture:{
                //"2":  {name:"toTop", arg:[]},
            },
            text: {// dragText
                "9":  {name:"copyText", arg:[]},
                "4":  {name:"deleteText", arg:[]},
                //"7":  {name:"cutText", arg:[]},
                "1":  {name:"pasteText", arg:[]},
                //"82":  {name:"space", arg:[]},
                "2": {
                    name:"openLinkText",
                    arg:["00", "10"]
                },
                "8":  {
                    name:"searchoropen",
                    arg:["https://www.baidu.com/s?wd=", "00", "10"]
                },
               "6":  {
                    name:"searchText",
                    arg:["https://www.google.com/search?q=", "00", "10"]
                },
                //"28": {name:"translateText", arg:[]}
            },
            link: {// drag link
                "3": {
                    name:"openLink",
                    arg:["00", "10"]
                },
                "9": {name:"copyLink", arg:[]},
                //"1": {name:"copyLinkText", arg:[]},
            },
            image: {// drag image
                "46": {name:"saveImg", arg:[]},
                "64": {name:"copyImg", arg:[]},
                "2": {
                    name:"openImgURL",
                    arg:["00", "10"]
                },
                "3": {
                    name:"searchImg",
                    arg:["https://graph.baidu.com/details?isfromtusoupc=1&tn=pc&carousel=0&promotion_name=pc_image_shituindex&extUiData%5bisLogoShow%5d=1&image=U-R-L", "00", "10"]
                },
                "7": {
                    name:"searchImg",
                    arg:["https://yandex.com/images/search?rpt=imageview&url=U-R-L", "00", "10"]
                },
                "4": {
                    name:"searchImg",
                    arg:["https://www.tineye.com/search?url=U-R-L", "00", "10"]
                },
                "6": {
                    name:"searchImg",
                    //arg:["https://www.google.com/searchbyimage?image_url=U-R-L", "00", "10"]
                    //arg:["https://www.google.com/searchbyimage?image_url=U-R-L&client=app", "00", "10"]
                    arg:["https://www.google.com/searchbyimage?sbisrc=cr_1_5_2&image_url=U-R-L", "00", "10"]
                    
                },
                "9": {name:"copyImgLink", arg:[]},
                //"1": {name:"copyImgURL", arg:[]},
                "8": {
                    name:"openImgLink",
                    arg:["00", "10"]
                },
                //"4": {name:"selectImg", arg:[]}
            },
        },
        cfg = storage.get('cfg',_cfg),
        Fn = {
            userDefine: function(argumentArr, data){
                try {
                    new Function("mpArray", "mpData", mpUnescape(argumentArr[0]))(data);
                } catch(e) {
                    console.log(e);
                }
            },
            /*stopLoading: function() {
                window.stop();
            },
            reload: function() {
                history.go(0);
                //window.location.reload();
            },
            reloadNoCache: function() {
                window.location.reload(true);
            },
            close: function() {
                window.close();
            },
            back: function() {
                history.back();
            },
            forward: function() {
                history.forward();
            },
            toTop: function() {
                document.documentElement.scrollTo(0, 0);
            },
            toBottom: function() {
                document.documentElement.scrollTo(0, 9999999);
            },
            reopenTab: function() {
                //GreasyMonkdy:
                // GM_openInTab(GM_getValue('latestTab'),false);
                //TamperMonkey:
                GM_openInTab(GM_getValue('latestTab', 'about:blank'), {
                    active: true
                });
            },
            URLLevelUp: function() {
                //当前网址的层次结构向上一层
                if (window.location.href[window.location.href.length - 1] === "/")
                    window.location.href = "../";
                else
                    window.location.href = "./";
            },
            //clone curren tab ,background
            cloneTab: function() {
                GM_openInTab(location.href, {
                    active: false
                });
            },
            //open new blank tab
            openBlankTab: function() {
                GM_openInTab('about:blank', {
                    active: true
                });
            },
            //view source
            viewSource: function() {
                GM_openInTab('view-source:'+location.href, {
                    active: true
                });
            },
            fkVip: function(argumentArr) {
                GM_openInTab(argumentArr[0]+location.href, {active:true});
            },
            closeOtherTabs: function() {
                GM_setValue('closeAll', Date());
            },*/

            deleteText: function() {
                try {
                    if(document.execCommand("Delete", "false", null)){
                        //success info
                        count(newFn.deleteText);
                        console.log("doSomethingOk");
                    } else{
                        //fail info
                        console.log("doSomethingNotOk");
                    }
                } catch (error) {
                    return document.execCommand("Delete", "false", null);
                }
            },
            cutText: function() {
                try {
                    if(document.execCommand("Cut", "false", null)){
                        count(newFn.cutText);
                        console.log("doSomethingOk");

                    } else{
                        //fail info
                        console.log("doSomethingNotOk");
                    }
                } catch (error) {
                    return document.execCommand("Cut", "false", null);
                }
            },
            pasteText: function() {
                try {
                    if(window.navigator.clipboard.readText()
                       .then(text => {
                        document.execCommand("insertText", "false", text);
                    })
                       .catch(err => {
                        console.error('Failed to read clipboard contents: ', err);
                    })) {
                        //success info
                        count(newFn.pasteText);
                        console.log("doSomethingOk");
                    } else{
                        //fail info
                        console.log("doSomethingNotOk");
                    }
                } catch (error) {
                    return;
                }
            },
            space: function() {
                try {

                    if(document.execCommand("insertText", "false", " ")){
                        count(newFn.space);
                        console.log("doSomethingOk");
                    } else{
                        //fail info
                        console.log("doSomethingNotOk");
                    }
                } catch (error) {
                    return document.execCommand("insertText", "false", " ");
                }
            },
            openLinkText: function(argumentArr, data) {
                var linkte = data.textSelection
                var linktex = linkte.replace(/(^\s*)|(\s*$)/g, "");
                var url = linktex.match(/^(?=.*chrome:).*$|^(?=.*edge:).*$|^(?=.*extension:).*$|^(?=.*115:).*$|((https|http)?:\/\/(\w[\w-]*\.)+[A-Za-z]{2,4}(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?|(\w[\w-]*\.)+(app|art|br|biz|com|cn|cc|co|cm|ci|ch|club|cyou|cloud|de|dev|edu|fm|fr|gb|gov|ga|gq|hk|info|in|im|io|int|icu|jp|li|la|ly|link|me|ml|moe|mobi|name|net|org|one|pro|pw|porn|ru|rip|red|sex|sexy|site|space|tv|tw|to|tk|today|top|us|uk|video|vip|world|wang|xxx|xin|xyz)(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?)/i);
                var cvalue = linkte.replace(/\r\n/g,"\n");
                var sarr = cvalue.split("");
                var len_total = sarr.length;
                var r={
                    "wd":0,//中英文字数
                    "nwd":0,//英数词数
                    "kwd":0,//日文假名
                    "krd":0,//韩文字
                    "nb":0,//数字词数
                    "c":0,//字符数
                    "cb":0,//非空格字符
                    "r":0,//回车
                    "en":0,//英文字母数
                    "cn":0,//中文字数
                    "bl":0//非回车空格
                };
                var words = cvalue.match(/\w+([’\']\w+)?/g)||[];//含撇号(如I'm)的单词视为一个词
                var numbers = cvalue.match(/\b\d+(\.\d+)?\b/g)||[];//含小数点的数字视为一个词
                var cnwords = cvalue.match(/[\u4e00-\u9fa5]/g)||[];//统一中文字范围
                var kanawds = cvalue.match(/[\u3040-\u30ff]/g)||[];//日文假名范围
                var krwords = cvalue.match(/[\uac00-\ud7af]/g)||[]; //韩文字范围
                r.nwd = words.length;
                r.nb = numbers.length;
                r.cn = cnwords.length;
                r.kwd = kanawds.length;
                r.krd = krwords.length;
                for(var i=0; i<len_total; i++){
                    r.c++;
                    switch(true){
                        case /[a-zA-Z]/.test(sarr[i]):
                            r.en++;
                            break;
                        case /\S/.test(sarr[i]):
                            r.cb++;
                            break;
                        case /\s/.test(sarr[i]):
                            if(sarr[i]=="\n"||sarr[i]=="\r"){
                                r.r++;
                            }else{
                                r.bl++;
                            }
                    }
                }
                if(url && linktex.indexOf(" ") == -1 && r.cn == 0 && linktex.indexOf(",") == -1 && linktex.indexOf(",") == -1) {
                    if(linktex.indexOf("http://")==0 || linktex.indexOf("https://")==0)
                    {
                        try {
                            if(argumentArr[0] != "02") {
                                GM_openInTab(linktex, {
                                    active: argumentArr[0] != "01",
                                    insert: argumentArr[1] != "11",
                                    setParent :true
                                });
                            } else if(argumentArr[0] == "02") {
                                window.open(linktex, '_self');
                            }
                            count(newFn.openLinkText);
                        } catch (error) {}
                    } else if(linktex.indexOf("chrome://")==0 || linktex.indexOf("edge://")==0 || linktex.indexOf("extension://")==0)
                    {
                        try {
                            GM_openInTab(linktex, {
                                active: argumentArr[0] != "01",
                                insert: argumentArr[1] != "11",
                                setParent :true
                            });
                            count(newFn.openLinkText);
                        } catch (error) {}
                    } else {
                        try {
                            if(argumentArr[0] != "02") {
                                GM_openInTab("http://"+linktex, {
                                    active: argumentArr[0] != "01",
                                    insert: argumentArr[1] != "11",
                                    setParent :true
                                });
                            } else if(argumentArr[0] == "02") {
                                window.open("http://"+linktex, '_self');
                            }
                            count(newFn.openLinkText);
                        } catch (error) {}
                    }
                } else {
                    console.log("Error")
                }

            },
            searchText: function(argumentArr, data) {
                if(argumentArr[1] != "02") {
                    GM_openInTab(argumentArr[0] + encodeURIComponent(data.textSelection), {
                        active: argumentArr[1] != "01",
                        insert: argumentArr[2] != "11",
                        setParent: true //makes the browser re-focus the current tab on close.
                    });
                } else if(argumentArr[1] == "02") {
                    window.open(argumentArr[0] + encodeURIComponent(data.textSelection), '_self');
                }
                //console.log(argumentArr[1]);
                count(newFn.searchText);
            },
            searchoropen: function(argumentArr, data) {
                var linkte = data.textSelection
                var linktex = linkte.replace(/(^\s*)|(\s*$)/g, "");
                var url = linktex.match(/^(?=.*chrome:).*$|^(?=.*edge:).*$|^(?=.*extension:).*$|^(?=.*115:).*$|((https|http)?:\/\/(\w[\w-]*\.)+[A-Za-z]{2,4}(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?|(\w[\w-]*\.)+(app|art|br|biz|com|cn|cc|co|cm|ci|ch|club|cyou|cloud|de|dev|edu|fm|fr|gb|gov|ga|gq|hk|info|in|im|io|int|icu|jp|li|la|ly|link|me|ml|moe|mobi|name|net|org|one|pro|pw|porn|ru|rip|red|sex|sexy|site|space|tv|tw|to|tk|today|top|us|uk|video|vip|world|wang|xxx|xin|xyz)(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?)/i);
                var cvalue = linkte.replace(/\r\n/g,"\n");//去掉换行符
                var sarr = cvalue.split("");
                var len_total = sarr.length;
                var r={
                    "wd":0,//中英文字数
                    "nwd":0,//英数词数
                    "kwd":0,//日文假名
                    "krd":0,//韩文字
                    "nb":0,//数字词数
                    "c":0,//字符数
                    "cb":0,//非空格字符
                    "r":0,//回车
                    "en":0,//英文字母数
                    "cn":0,//中文字数
                    "bl":0//非回车空格
                };
                var words = cvalue.match(/\w+([’\']\w+)?/g)||[];//含撇号(如I'm)的单词视为一个词
                var numbers = cvalue.match(/\b\d+(\.\d+)?\b/g)||[];//含小数点的数字视为一个词
                var cnwords = cvalue.match(/[\u4e00-\u9fa5]/g)||[];//统一中文字范围
                var kanawds = cvalue.match(/[\u3040-\u30ff]/g)||[];//日文假名范围
                var krwords = cvalue.match(/[\uac00-\ud7af]/g)||[]; //韩文字范围
                r.nwd = words.length;
                r.nb = numbers.length;
                r.cn = cnwords.length;
                r.kwd = kanawds.length;
                r.krd = krwords.length;
                for(var i=0; i<len_total; i++){
                    r.c++;
                    switch(true){
                        case /[a-zA-Z]/.test(sarr[i]):
                            r.en++;
                            break;
                        case /\S/.test(sarr[i]):
                            r.cb++;
                            break;
                        case /\s/.test(sarr[i]):
                            if(sarr[i]=="\n"||sarr[i]=="\r"){
                                r.r++;
                            }else{
                                r.bl++;
                            }
                    }
                }
                if(url && linktex.indexOf(" ") == -1 && r.cn == 0 && linktex.indexOf(",") == -1 && linktex.indexOf(",") == -1) {
                    if(linktex.indexOf("http://") == 0 || linktex.indexOf("https://") == 0)
                    {
                        try {
                            if(argumentArr[1] != "02") {
                                GM_openInTab(linktex, {
                                    active: argumentArr[1] != "01",
                                    insert: argumentArr[2] != "11",
                                    setParent :true
                                });
                            } else if(argumentArr[1] == "02") {
                                window.open(linktex, '_self');
                                //window.location.href = linktex;
                            }
                        } catch (error) {}
                    } else if(linktex.indexOf("chrome://") == 0 || linktex.indexOf("edge://") == 0 || linktex.indexOf("extension://") == 0)
                    {
                        try {
                            GM_openInTab(linktex, {
                                active: argumentArr[1] != "01",
                                insert: argumentArr[2] != "11",
                                setParent :true
                            });
                        } catch (error) {}
                    } else {
                        try {
                            if(argumentArr[1] != "02") {
                                GM_openInTab("http://" + linktex, {
                                    active: argumentArr[1] != "01",
                                    insert: argumentArr[2] != "11",
                                    setParent :true
                                });
                            } else if(argumentArr[1] == "02") {
                                window.open("http://" + linktex, '_self');
                                //window.location.href = "http://" + linktex;
                            }
                        } catch (error) {}
                    }
                    count(newFn.openLinkText);
                } else {
                    //console.log("Error")
                    if(argumentArr[1] != "02") {
                        GM_openInTab(argumentArr[0] + encodeURIComponent(data.textSelection).replaceAll('%C2%A0','%20'),
                                     {
                            active: argumentArr[1] != "01",
                            insert: argumentArr[2] != "11",
                            setParent: true //makes the browser re-focus the current tab on close.
                        });
                    } else if(argumentArr[1] == "02") {
                        window.open(argumentArr[0] + encodeURIComponent(data.textSelection), '_self');
                        //window.location.href = argumentArr[0] + encodeURIComponent(data.textSelection)
                    }
                    count(newFn.searchText);
                }

            },
            //translateText: function(argumentArr, data) {
            //    showclipboardx(data.textSelection);
            //    count(newFn.translateText);
            //},
            copyText: function(argumentArr, data) {
                GM_setClipboard(data.textSelection, "text");
                //showclipboard(data.textSelection);
                window.navigator.clipboard.readText()
                    .then(text => {
                    showclipboard(text);
                })
                    .catch(err => {
                    console.error('Failed to read clipboard contents: ', err);
                });
                count(newFn.copyText);
            },
            openLink: function(argumentArr, data) {
                //TamperMonkey
                if(argumentArr[0] != "02") {
                    GM_openInTab(getLink(data), {
                        active: argumentArr[0] != "01",
                        insert: argumentArr[1] != "11",
                        setParent :true
                    });
                } else if(argumentArr[0] == "02") {
                    window.open(getLink(data), '_self');
                }
                console.log(argumentArr[0])
                count(newFn.openLink);
            },
            copyLink: function(argumentArr, data) {
                GM_setClipboard(getLink(data), "text");

                //showclipboard(getLink(data));
                window.navigator.clipboard.readText()
                    .then(text => {
                    showclipboard(text);
                })
                    .catch(err => {
                    console.error('Failed to read clipboard contents: ', err);
                });
                count(newFn.copyLink);

            },
            copyLinkText: function(argumentArr, data) {
                GM_setClipboard(data.target.textContent || data.textSelection, "text");

                //showclipboard(data.target.textContent || data.textSelection);
                window.navigator.clipboard.readText()
                    .then(text => {
                    showclipboard(text);
                })
                    .catch(err => {
                    console.error('Failed to read clipboard contents: ', err);
                });
                count(newFn.copyLinkText);
            },

            copyImgLink: function(argumentArr, data) {
                GM_setClipboard(getLink(data), "text");

                window.navigator.clipboard.readText()
                    .then(text => {
                    showclipboard(text);
                })
                    .catch(err => {
                    console.error('Failed to read clipboard contents: ', err);
                });
                count(newFn.copyImgLink);
            },
            openImgLink: function(argumentArr, data) {
                if(argumentArr[0] != "02") {
                    GM_openInTab(getLink(data), {
                        active: argumentArr[0] != "01",
                        insert: argumentArr[1] != "11",
                        setParent :true
                    });
                } else if(argumentArr[0] == "02") {
                    window.open(getLink(data), '_self');
                }
                count(newFn.openImgLink);
            },
            saveImg: function(argumentArr, data) {
                //TamperMonkey
                let name = data.target.src.split('/').pop();

                let d = new Date();
                let TimeDateFormatText = '[Year]-[Month]-[Day] [Hour][Minute][Second]';
                let timetext = 'image-'+TimeDateFormatText.replace(/\[YEAR\]/gi, d.getFullYear().toString()).replace(/\[MONTH\]/gi, ('0' +(d.getMonth()+1).toString()).slice(-2)).replace(/\[DAY\]/gi, ('0' +d.getDate().toString()).slice(-2)).replace(/\[HOUR\]/gi, ('0' +d.getHours().toString()).slice(-2)).replace(/\[MINUTE\]/gi, ('0' +d.getMinutes().toString()).slice(-2)).replace(/\[SECOND\]/gi, ('0' +d.getSeconds().toString()).slice(-2));

                GM_download(data.target.src, timetext);
                showclipboard("已保存");
                count(newFn.saveImg);
                //method 2
                /*
        let a = document.createElement('a');
        a.href = dObj.img; a.setAttribute('download', dObj.img.split('/').pop());
        document.documentElement.appendChild(a);
        a.click();
        a.parentElement.remove(a);
        */
                /* //jQuery:
        $("<a>").attr("href", actionFn.request.selimg).attr("download", actionFn.request.selimg.split('/').pop()).appendTo("body");
        a[0].click();
        a.remove();
        */
            },
            searchImg: function(argumentArr, data) {
                //TamperMonkey
                if(argumentArr[1] != "02") {
                GM_openInTab(argumentArr[0].replace(/U-R-L/, data.target.src), {
                    active: argumentArr[1] != "01",
                    insert: argumentArr[2] != "11",
                    setParent: true
                });
                } else if(argumentArr[1] == "02") {
                    window.open(argumentArr[0].replace(/U-R-L/, data.target.src), '_self');
                }
                count(newFn.searchImg);
            },
            /*selectImg: function(argumentArr, data) {
                // it may not working on some browsers [develping standard]
                //TamperMonkey
                document.execCommand('selectAll');
                let sel = document.getSelection();
                sel.collapse(data.target.self, 0);
                sel.modify("extend", "forward", "character");
            },*/
            copyImg: function(argumentArr, data) {
            /*let canvas = canvasDrawTheImage(e);
            // get image as blob
            canvas.canvas.toBlob((blob) => {
                GM_setClipboard(blob, {
                    type: canvas.type,
                    mimetype: canvas.mime
                });
            }, canvas.mime);*/
                //copyimagetoclip (data.target.src);

                copyImageto(data.target.src);

                //console.log("OK")
                count(newFn.copyImg);
            },
        /*image2DataURL: function(e) {
            //canvas绘制图片,由于浏览器的安全考虑:
            //如果在使用canvas绘图的过程中,使用到了外域的图片资源,那么在toDataURL()时会抛出安全异常:
            let canvas = canvasDrawTheImage(e).canvas;
            let dataURL = canvas.toDataURL();
            GM_setClipboard(dataURL, "text");
        },*/
            copyImgURL: function(argumentArr, data) {
                //TamperMonkey
                GM_setClipboard(data.target.src, "text");

                showclipboard(data.target.src);
                count(newFn.copyImgURL);
            },
            openImgURL: function(argumentArr, data) {
                //TamperMonkey
                if(argumentArr[0] != "02") {
                    GM_openInTab(data.target.src, {
                        active: argumentArr[0] != "01",
                        insert: argumentArr[1] != "11",
                        //active: false,
                        //insert: true,
                        setParent :true
                    });
                } else if(argumentArr[0] == "02") {
                    window.open(data.target.src, '_self');
                }
                count(newFn.openImgURL);
            },
            setting: function() {
                if (document.getElementById('MPsetting')) {
                    return;
                }else Ui.init();
            }
        },
        local = {
            gesture:{
                /*stopLoading:     {zh:'停止加载',         en:'StopLoading'},
                reload:          {zh:'刷新',             en:'Refresh'},
                reloadNoCache:   {zh:'清缓存刷新',       en:'Refresh Without Cache'},
                close:           {zh:'关闭',             en:'Close'},
                back:            {zh:'后退',             en:'Back'},
                forward:         {zh:'前进',             en:'Forward'},
                toTop:           {zh:'到顶部',           en:'Scroll to Top'},
                toBottom:        {zh:'到底部',           en:'Scroll to Bottom'},
                reopenTab:       {zh:'打开最近关闭窗口', en:'Reopen Latest Closed Window'},
                setting:         {zh:'设置',             en:'Settings'},
                URLLevelUp:      {zh:'网址向上一层',     en:'URL hierarchy up'},
                cloneTab:        {zh:'克隆标签页',       en:'Duplicate This Tab'},
                openBlankTab:    {zh:'打开空白页',       en:'Open New Blank Tab'},
                viewSource:      {zh:'看网页源代码',     en:'View Source'},
                fkVip:           {zh:'破解VIP视频',      en:'Crack to Watch VIP Video'},
                closeOtherTabs:  {zh:'关闭其他标签',     en:'Close Other Tabs'},
                translateSelect: {zh:'开启划词翻译',     en:'Turn on Select And Translate'},
                //开发者功能
                contentEditable: {zh:'元素内容可编辑',   en:'Element Content Editable'},
                userDefine:      {zh:'自定义',           en:'User Define'}*/
            },
            //drag text
            text: {
                searchText:      {zh:'搜索',     en:'Search Selected Text'},
                searchoropen:     {zh:'搜索文字、打开链接',             en:'Search Selected Text'},
                copyText:        {zh:'复制',     en:'Copy Selected Text'},
                deleteText:      {zh:'退格',             en:'Delete Selected Text'},
                cutText:         {zh:'剪切',             en:'Cut Selected Text'},
                pasteText:       {zh:'粘贴',             en:'Paste Selected Text'},
                space:           {zh:'空格',             en:'Input Space'},
                openLinkText:    {zh:'打开链接(文本)',   en:'Open Link Text'},
                translateText:   {zh:'翻译',             en:'Translate'},
                userDefine:      {zh:'自定义',           en:'User Define'}
            },
            //drag link
            link:{
                openLink:        {zh:'打开链接',         en:'Open Link'},
                copyLink:        {zh:'复制链接',         en:'Copy Link'},
                copyLinkText:    {zh:'复制链接文字',     en:'Copy Link Text'},
                userDefine:      {zh:'自定义',           en:'User Define'}
            },
            //drag image
            image:{
                saveImg:         {zh:'保存图片',         en:'Save Image'},
                searchImg:       {zh:'搜索图片',         en:'Search Image'},
                copyImg:         {zh:'复制图片',         en:'Copy Image to ClickBoard'},
                copyImgURL:      {zh:'复制图片链接(img)',     en:'Copy ImageURL'},
                openImgURL:      {zh:'新标签打开图片(img)',   en:'Open ImageURL'},
                copyImgLink:     {zh:'复制图片链接',     en:'Copy ImageLink'},
                openImgLink:     {zh:'打开图片链接',     en:'Open ImageLink'},
                // image2DataURL:   {zh:'复制图片为DataURL',en:'Copy Image as DataURL'},
                //selectImg:       {zh:'选中图片',         en:'Select This Image'},
                userDefine:      {zh:'自定义',           en:'User Define'}
            }
        };

    GM_registerMenuCommand("设置", function () {
        if (document.getElementById('MPsetting')) {
            return;
        }else Ui.init();
    });
            
    let mouseEvent = null;

    document.addEventListener('mousemove', event => {
        mouseEvent = event;
    });

    document.addEventListener('keydown',function(event) {

        var keynum;
        if(window.event) // IE
            keynum = event.keyCode;
        else if(event.which) // Netscape/Firefox/Opera
            keynum = event.which;

        if(keynum == 89 && event.altKey) {//Alt+Y设置
            if (document.getElementById('MPsetting')) {
                return;
            }else Ui.init();
        }else if(keynum ==73 && event.altKey) {//Alt+I重置
            var mymessage = confirm("是否确定重置设置?");
            if(mymessage == true) {
                GM_deleteValue('cfg');
            }
        }else if(keynum == 27) {//Esc退出设置
            Ui.closesetting()
        }else if(keynum ==85 && event.altKey) {//Alt+U计数
            let allValue = GM_listValues();
            let vala = [];
            let valb = [];
            let valc = [];
            var cnname;
            allValue.forEach(function(value, index) {//["searchText","Google搜索"]

                     Object.keys(local).forEach(function (value1, index1) {//[iconData[iconArraya], iconDara[iconArrayb]]

                        Object.keys(local[value1]).forEach(function(n) {//[name:,image:,host:][name:,image:,host:]

                            cnname = local[value1][n].zh;


                            if(index1 == 1){
                                if(value === cnname){
                                    vala.push(GM_getValue(value));
                                }
                            } else if(index1 == 2){
                                if(value === cnname){
                                    valb.push(GM_getValue(value));
                                }
                            } else if(index1 == 3){
                                if(value === cnname){
                                    valc.push(GM_getValue(value));
                                }
                            }

                    });
                });
            });

            vala.sort(compare( "times"));
            valb.sort(compare( "times"));
            valc.sort(compare( "times"));

            console.log("文字功能统计数据:");
            console.log(vala);
            console.log("链接功能统计数据:");
            console.log(valb);
            console.log("图片功能统计数据:");
            console.log(valc);

        }

    });

    //========②supported functions=======
    function getLink(data){
        if(data.link)
            return data.link.href;
        else if(data.target.src)
            return data.target.src;
        //else return data.textSelection;
    }

    //--> check if string is an url
    function isURL (string) {
        var url = string.match(/^(?=.*chrome:).*$|^(?=.*edge:).*$|^(?=.*extension:).*$|^(?=.*115:).*$|^(?=.*data:).*$|((https|http)?:\/\/(\w[\w-]*\.)+[A-Za-z]{2,4}(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?|(\w[\w-]*\.)+(app|art|br|biz|com|cn|cc|co|cm|ci|ch|club|cyou|cloud|de|dev|edu|fm|fr|gb|gov|ga|gq|hk|info|in|im|io|int|icu|jp|li|la|ly|link|me|ml|moe|mobi|name|net|org|one|pro|pw|porn|ru|rip|red|sex|sexy|site|space|tv|tw|to|tk|today|top|us|uk|video|vip|world|win|wang|xxx|xin|xyz)(?!\w)(:\d+)?(\/([\x21-\x7e]*[\w\/=])?)?)/i);
        return url;
    }

    //--> checks if the current window is framed or not
    function inIframe () {
        try {
            return window.self !== window.top;
        }
        catch (e) {
            return true;
        }
    }

    //--> returns all available data of the given target
    //--> this data is used by some background actions
    function getTargetData(target) {
        let data = {};

        data.target = {
            src: target.currentSrc || target.src || null,
            title: target.title || null,
            alt: target.alt || null,
            textContent: target.textContent.trim(),
            nodeName: target.nodeName,
            self: target
        };

        let link = getClosestLink(target);
        if (link) {
            data.link = {
                href: link.href || null,
                title: link.title || null,
                textContent: link.textContent.trim()
            };
        }

        data.textSelection = getTextSelection();

        return data;
    }

    //--> returns the selected text, if no text is selected it will return an empty string
    //--> inspired by https://stackoverflow.com/a/5379408/3771196
    function getTextSelection () {
        // get input/textfield text selection
        if (document.activeElement &&
            typeof document.activeElement.selectionStart === 'number' &&
            typeof document.activeElement.selectionEnd === 'number') {
            return document.activeElement.value.slice(
                document.activeElement.selectionStart,
                document.activeElement.selectionEnd
            );
        }
        // get normal text selection
        return window.getSelection().toString();
    }

    //--> calculates and returns the distance
    //--> between to points
    function getDistance(x1, y1, x2, y2) {
        return Math.hypot(x2 - x1, y2 - y1);
    }

    //--> returns the closest hierarchical link node or null of given element
    function getClosestLink (node) {
        // bubble up the hierarchy from the target element
        while (node !== null && node.nodeName.toLowerCase() !== "a" && node.nodeName.toLowerCase() !== "area")
            node = node.parentElement;
        return node;
    }
    function getDirection(x, y, cx, cy){
        /*=================
    |                 |
    | 1↖   2↑   3↗ |
    |                 |
    | 4←    5    6→ |
    |                 |
    | 7↙   8↓   9↘ |
    |                 |
    |=================*/
        let d, t;
        if(cfg.directions == 4){   //4 directions
            if (Math.abs(cx - x) < Math.abs(cy - y)) {
                d = cy > y ? "8" : "2";
            } else {
                d = cx > x ? "6" : "4";
            }
        }else{  //8 directions
            t = (cy-y)/(cx-x);
            if     (-0.4142<= t && t < 0.4142) d = cx > x ? '6' : "4";
            else if(2.4142 <= t || t< -2.4142) d = cy > y ? '8' : '2';
            else if(0.4142 <= t && t < 2.4142) d = cx > x ? '9' : '1';
            else                               d = cy > y ? '7' : '3';
        }
        return d;
    }
    // data: data.data
    function getDragFn(data){
        // let
        if(data.target.nodeName === "IMG")
            return ['image',cfg.image[data.gesture]];
        //else if(data.link || data.target.nodeName === "A" || isURL(data.textSelection))
        else if((data.link || data.target.nodeName === "A") && data.textSelection == '')
            return ['link', cfg.link[data.gesture]];
        else
            return ['text', cfg.text[data.gesture]];
    }

    function mpEscape(str){
        if(!str) return;
        return str.replace(/"/g, "&quot;").replace(/'/g, "&apos;");
    }
    function mpUnescape(str){
        if(!str) return;
        return str.replace(/&quot;/g,'"').replace(/&apos;/g, "'");
    }

   function count(a) {
        //存储次数和日期
        let d = new Date();
        let TimeDateFormatText = '[Year]/[Month]/[Day] [Hour]:[Minute]:[Second]';
        let timetext = TimeDateFormatText.replace(/\[YEAR\]/gi, d.getFullYear().toString()).replace(/\[MONTH\]/gi, ('0' +(d.getMonth()+1).toString()).slice(-2)).replace(/\[DAY\]/gi, ('0' +d.getDate().toString()).slice(-2)).replace(/\[HOUR\]/gi, ('0' +d.getHours().toString()).slice(-2)).replace(/\[MINUTE\]/gi, ('0' +d.getMinutes().toString()).slice(-2)).replace(/\[SECOND\]/gi, ('0' +d.getSeconds().toString()).slice(-2));

        var b;
        Object.keys(local).forEach(function (value1, index1) {
            Object.keys(local[value1]).forEach(function(n) {

                if(a === n){
                    b = local[value1][n].zh;
                }
            });
        });

        if(GM_getValue(b)){
            GM_setValue(b, {
                'name':b,
                'times': GM_getValue(b).times + 1,
                'date': timetext
            });
        }else{
            GM_setValue(b, {
                'name':b,
                'times': 1,
                'date': timetext
            });
        }

        console.log(b + ":" + GM_getValue(b).times + "times\0" + GM_getValue(b).date);

    }

    function compare( propertyName) {
        return function( object1, object2) {
            var value1 = object1[propertyName];
            var value2 = object2[propertyName];
            if(value1 < value2) {
                return 1;
            } else if(value1 > value2) {
                return - 1;
            } else {
                return 0;
            }
        }
    }

    //对象的元素中获取该元素的名字
    const handler = {
        get: function(obj, prop) {
            //console.log(prop);
            //return obj[prop];
            return prop;
        }
    };

    const newFn = new Proxy(Fn, handler);


    //复制图片到剪切板
    function imageToBlob(imageURL) {
        const img = new Image;
        const c = document.createElement("canvas");
        const ctx = c.getContext("2d");
        img.crossOrigin = "";
        //img.src = imageURL;
        img.src = imageURL + '?v=' + Math.random();
        return new Promise(resolve => {
            img.onload = function () {
                c.width = this.naturalWidth;
                c.height = this.naturalHeight;
                ctx.drawImage(this, 0, 0);
                c.toBlob((blob) => {
                    // here the image is a blob
                    resolve(blob)
                }, "image/png", 0.75);
            };
        })
    }

    async function copyImageto(imageURL){
        const blob = await imageToBlob(imageURL)
        const item = new ClipboardItem({ "image/png": blob });
        navigator.clipboard.write([item]);
        showclipboard("已复制");
    }



    //========③Hinter====================
    const Hinter = (function(){
        let modul = {};

        modul.enable = function enable(){
            GestureHandler
                .on("start", addCanvas)
                .on("update", updateTrack)
                .on("change", updateHint)
                .on("abort", reset)
                .on("end", reset);
        };

        modul.applySettings = function applySettings(Config){
            //background   = Config.Hinter.background;//隐藏提示
            fontSize     = Config.Hinter.fontSize;
            //lineColor    = Config.Hinter.lineColor;
            minLineWidth = Config.Hinter.minLineWidth;
            maxLineWidth = Config.Hinter.maxLineWidth;
            //lineGrowth   = Config.Hinter.lineGrowth;
            funNotDefine = Config.Hinter.funNotDefine;
            updateHintLayer();
        };

        //private methods & value
        let
        background = '',//隐藏提示
            fontSize = 0,
            lineColor = null,
            minLineWidth = 1,
            maxLineWidth = 10,
            lineGrowth = 0.6,
            funNotDefine = '';
        let canvas = null,
            tip = null,
            ctx = null,
            hasCanvas = false;

        function updateHintLayer(){
            canvas = tip = ctx = hasCanvas = null;
            createCanvaTips();
        }
        function createCanvaTips(){
            //create <canvas>
            canvas = document.createElement("canvas");
            canvas.id = 'MPcanvas';
            ctx = canvas.getContext("2d");
            //create tips<div>
            tip = document.createElement('div');
            tip.id = 'MPtips';
            tip.style.cssText = `background:#${background} !important;  font-size: ${fontSize}px !important;`;
        }
        //<canvas> & tip<div> is ready, when mousemove or drag, append to show track & tips
        function addCanvas(e) {
            if(!canvas || !tip) createCanvaTips();
            document.documentElement.appendChild(tip);      //append tip <div>
            document.documentElement.appendChild(canvas);   //append <canvas>
            canvas.width = window.innerWidth;               //set canvas attribute to clear content
            canvas.height = window.innerHeight;
            ctx.lineCap = "round";
            ctx.lineJoin = "round";
            //if(lineColor.length>6) canvas.style.opacity = parseInt(lineColor.slice(6),16)/255;
            canvas.style.opacity = 0;//不显示轨迹
            ctx.lineWidth = minLineWidth;
            //ctx.strokeStyle = '#' + lineColor.slice(0,6); //like delicious link color//line color
            hasCanvas = true;
            //allow drop
            tip.addEventListener('dragover', ()=>event.preventDefault(), false);
            canvas.addEventListener('dragover', ()=>event.preventDefault(), false);
        }
        //remove <canvas> and tips<div>,set flags to false
        function reset() {
            if (hasCanvas) {
                document.documentElement.removeChild(canvas);
                tip.innerHTML = '';
                document.documentElement.removeChild(tip);
            }
            hasCanvas = false;
        }
        //show Tips
        function updateHint(gesture,fnName){
            tip.innerHTML = gesture.join("") + '<br/>' + (fnName ? fnName : funNotDefine);
        }
        function updateTrack(x,y){
            if (hasCanvas) {
                ctx.lineWidth = Math.min(maxLineWidth, ctx.lineWidth += lineGrowth);
                ctx.lineTo(x, y);
                ctx.stroke();
                ctx.closePath();
                ctx.beginPath();
                ctx.moveTo(x, y);
            }
        }
        // due to modul pattern: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
        return modul;
    })();

    //========④GesturedHadler============
    //--> GestureHandler "singleton" class using the modul pattern
    //--> the handler behaves different depending on whether it's injected in a frame or not
    //--> frame: detects gesture start, move, end and sends an indication message
    //--> main page: detects whole gesture including frame indication messages and reports it to the background script
    //--> provides 4 events: on start, update, change and end
    //--> on default the handler is disabled and must be enabled via enable()
    //--> REQUIRES: contentCommons.js
    const GestureHandler = (function() {
        // public variables and methods
        let modul = {};

        //-->Add callbacks to the given events
        modul.on = function on(event, callback) {
            // if event does not exist or function already applied skip it
            if (event in events && !events[event].includes(callback))
                events[event].push(callback);
            return this;
        };

        //-->applies necessary settings
        modul.applySettings = function applySettings(Settings) {
            mouseButton = Number(Settings.Gesture.mouseButton);
            suppressionKey = Settings.Gesture.suppressionKey;
            distanceSensitivity = Settings.Gesture.distanceSensitivity;
            distanceThreshold = Settings.Gesture.distanceThreshold;
            timeoutActive = Settings.Gesture.Timeout.active;
            timeoutDuration = Settings.Gesture.Timeout.duration;
        };

        //-->Add the event listeners
        modul.enable = function enable() {
            if (inIframe()) {
                //window.addEventListener('mousedown', handleFrameMousedown, true);//去掉鼠标手势动作
                //window.addEventListener('mousemove', handleFrameMousemove, true);
                //window.addEventListener('mouseup', handleFrameMouseup, true);
                window.addEventListener('mousedown', handleMousedown, true);
                window.addEventListener('dragstart', handleDragstart, true);
            } else {
                // chrome.runtime.onMessage.addListener(handleMessage);
                window.addEventListener('mousedown', handleMousedown, true);
            }
        };

        //-->Remove the event listeners and resets the handler
        modul.disable = function disable() {
            if (inIframe()) {
                //window.removeEventListener('mousedown', handleFrameMousedown, true);
                //window.removeEventListener('mousemove', handleFrameMousemove, true);
                //window.removeEventListener('mouseup', handleFrameMouseup, true);
                window.removeEventListener('mousedown', handleMousedown, true);
                window.removeEventListener('dragstart', handleDragstart, true);
            } else {
                // chrome.runtime.onMessage.removeListener(handleMessage);
                window.removeEventListener('mousedown', handleMousedown, true);
                //window.removeEventListener('mousemove', handleMousemove, true);
                //window.removeEventListener('mouseup', handleMouseup, true);
                //window.removeEventListener('contextmenu', handleContextmenu, true);
                //window.removeEventListener('mouseout', handleMouseout, true);
                window.removeEventListener('dragstart', handleDragstart, true);
                // reset gesture array, internal state and target data
                directions = [];
                state = "passive";
                targetData = {};
            }
        };

        // private variables and methods

        // setting properties
        let mouseButton = 2,
            dragButton = 1,//MP
            suppressionKey = "",
            distanceThreshold = 2,
            distanceSensitivity = 10,
            timeoutActive = true, //超时取消动作 没取消轨迹
            timeoutDuration = 1;

        // contains all gesture direction letters
        let directions = [];

        // internal state: passive, pending, active
        let state = "passive";

        // holds reference point to current point
        let referencePoint = {
            x: 0,
            y: 0
        };

        // contains the timeout identifier
        let timeout = null;

        // contains relevant data of the target element
        let targetData = {};

        // holds all event callbacks added by on()
        let events = {
            'start': [],
            'update': [],
            'change': [],
            'abort': [],
            'end': []
        };

        //-->initializes the gesture to the "pending" state, where it's unclear if the user is starting a gesture or not
        //-->requires the current x and y coordinates
        function init(x, y) {
            // set the initial point
            referencePoint.x = x;
            referencePoint.y = y;

            // change internal state
            state = "pending";

            // add gesture detection listeners
            //window.addEventListener('mousemove', handleMousemove, true);
            window.addEventListener('dragstart', handleDragstart, true);
            window.addEventListener('drag', handleDrag, true);//MP
            window.addEventListener('dragend', handleDragend, true);//MP
            //window.addEventListener('contextmenu', handleContextmenu, true);
            //window.addEventListener('mouseup', handleMouseup, true);
            //window.addEventListener('mouseout', handleMouseout, true);
        }

        //-->Indicates the gesture start and should only be called once untill gesture end
        function start() {
            // dispatch all binded functions with the current x and y coordinates as parameter on start
            events['start'].forEach((callback) => callback(referencePoint.x, referencePoint.y));

            // change internal state
            state = "active";
        }

        //-->Indicates the gesture change and should be called every time the cursor position changes
        //-->requires the current x and y coordinates
        function update(x, y, dragMark) {
            // dispatch all binded functions with the current x and y coordinates as parameter on update
            events['update'].forEach((callback) => callback(x, y));

            // handle timeout
            if (timeoutActive) {
                // clear previous timeout if existing
                if (timeout) window.clearTimeout(timeout);
                timeout = window.setTimeout(() => {
                    // dispatch all binded functions on abort
                    events['abort'].forEach((callback) => callback());
                    state = "expired";
                    // clear directions
                    directions = [];
                    console.log("OK")
                }, timeoutDuration * 1000);
            }

            let direction = getDirection(referencePoint.x, referencePoint.y, x, y);

            if (directions[directions.length - 1] !== direction) {
                // add new direction to gesture list
                directions.push(direction);

                // send message to background on gesture change
                let message = runtime.sendMessage({
                    // subject: "gestureChange",
                    subject: dragMark ? "dragChange" : "gestureChange",//MP
                    data: Object.assign(//MP
                        targetData, {
                            gesture: directions.join("")
                        })
                });
                // on response (also fires on no response) dispatch all binded functions with the directions array and the action as parameter
                message.then((response) => {
                    let action = response ? response.action : null;
                    events['change'].forEach((callback) => callback(directions, action));
                });
            }

            // set new reference point
            referencePoint.x = x;
            referencePoint.y = y;
        }

        //-->Indicates the gesture end and should be called to terminate the gesture
        function end(dragMark) {
            // dispatch all binded functions on end
            events['end'].forEach((callback) => callback(directions));

            // send directions and target data to background if directions is not empty
            if (directions.length) runtime.sendMessage({
                // subject: "gestureEnd",
                subject: dragMark ? "dragEnd" : "gestureEnd",
                data: Object.assign(
                    targetData, {
                        gesture: directions.join("")
                    }
                )
            });

            // reset gesture handler
            reset();
        }

        //-->Resets the handler to its initial state
        function reset() {
            // remove gesture detection listeners
            //window.removeEventListener('mousemove', handleMousemove, true);
            //window.removeEventListener('mouseup', handleMouseup, true);
            //window.removeEventListener('contextmenu', handleContextmenu, true);
            //window.removeEventListener('mouseout', handleMouseout, true);
            window.removeEventListener('dragstart', handleDragstart, true);
            window.removeEventListener('drag', handleDrag, true);//MP
            window.removeEventListener('dragend', handleDragend, true);//MP

            // reset gesture array, internal state and target data
            directions = [];
            state = "passive";
            targetData = {};

            if (timeout) {
                window.clearTimeout(timeout);
                timeout = null;
            }
        }

        //-->Handles iframe/background messages which will update the gesture
        function handleMessage(message, sender, sendResponse) {

            switch (message.subject) {
                case "gestureFrameMousedown":
                    // init gesture
                    init(
                        Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
                        Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
                    );
                    // save target data
                    targetData = message.data;
                    break;

                case "gestureFrameMousemove":
                    // calculate distance between the current point and the reference point
                    let distance = getDistance(referencePoint.x, referencePoint.y,
                                               Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
                                               Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
                                              );
                    // induce gesture
                    if (state === "pending" && distance > distanceThreshold)
                        start();
                    // update gesture && mousebutton fix: right click on frames is sometimes captured by both event listeners which leads to problems
                    else if (state === "active" && distance > distanceSensitivity && mouseButton !== 2) update(
                        Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
                        Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
                    );
                    break;

                case "gestureFrameMouseup":
                    if (state === "active" || state === "expired") end();
                    else if (state === "pending") reset();
                    break;
            }
        }

        //-->Handles mousedown which will add the mousemove listener
        function handleMousedown(event) {
            // on mouse button and no supression key
            if (event.isTrusted && (event.buttons === mouseButton || event.buttons === dragButton) && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {//MP
                // init gesture
                init(event.clientX, event.clientY);

                // save target to global variable if exisiting
                if (typeof TARGET !== 'undefined') TARGET = event.target;

                // get and save target data
                targetData = getTargetData(event.target);

                // prevent and middle click scroll
                if (mouseButton === 4) event.preventDefault();
            }
        }

        //-->Handles mousemove which will either start the gesture or update it
        function handleMousemove(event) {
            if (event.isTrusted && event.buttons === mouseButton) {
                // calculate distance between the current point and the reference point
                let distance = getDistance(referencePoint.x, referencePoint.y, event.clientX, event.clientY);

                // induce gesture
                if (state === "pending" && distance > distanceThreshold)
                    start();

                // update gesture
                else if (state === "active" && distance > distanceSensitivity)
                    update(event.clientX, event.clientY);

                // prevent text selection
                if (mouseButton === 1) window.getSelection().removeAllRanges();
            }
        }

        //-->Handles context menu popup and removes all added listeners
        function handleContextmenu(event) {
            if (event.isTrusted && mouseButton === 2) {
                if (state === "active" || state === "expired") {
                    // prevent context menu
                    event.preventDefault();
                    end();
                }
                // reset if state is pending
                else if (state === "pending")
                    reset();
            }
        }

        //-->Handles mouseup and removes all added listeners
        function handleMouseup(event) {
            // only call on left and middle mouse click to terminate gesture
            if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4))) {
                if (state === "active" || state === "expired")
                    end();
                // reset if state is pending
                else if (state === "pending")
                    reset();
            }
        }

        //-->Handles mouse out and removes all added listeners
        function handleMouseout(event) {
            // only call if cursor left the browser window
            if (event.isTrusted && event.relatedTarget === null) {
                if (state === "active" || state === "expired")
                    end();
                // reset if state is pending
                else if (state === "pending")
                    reset();
            }
        }

        //-->Handles dragstart and prevents it if needed
        function handleDragstart(event) {
            // prevent drag if mouse button and no supression key is pressed
            if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey])))
                event.preventDefault();
        }

        //-->Handles drag  MP
        function handleDrag(event) {
            // prevent drag if mouse button and no supression key is pressed
            if (event.isTrusted && event.buttons === dragButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))){
                let distance = getDistance(referencePoint.x, referencePoint.y, event.clientX, event.clientY);

                // induce gesture
                if (state === "pending" && distance > distanceThreshold)
                    start();

                // update gesture
                else if (state === "active" && distance > distanceSensitivity)
                    update(event.clientX, event.clientY, 'dragMark');
            }
        }

        //-->Handles dragsend MP
        function handleDragend(event) {
            if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4) || event.button === 0 && dragButton === 1)) {//MP
                // if (event.isTrusted && ((event.button === 0 && gestureHandler.mouseButton === 1) || (event.button === 1 && gestureHandler.mouseButton === 4))) {
                if (state === "active" || state === "expired")
                    end("dragMark");
                // reset if state is pending
                else if (state === "pending")
                    reset();
            }
        }

        //-->Handles mousedown for frames; send message with target data and position
        function handleFrameMousedown(event) {
            // on mouse button and no supression key
            if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {
                runtime.sendMessage({
                    subject: "gestureFrameMousedown",
                    data: Object.assign(
                        getTargetData(event.target), {
                            screenX: event.screenX,
                            screenY: event.screenY,
                        }
                    )
                });
                // save target to global variable if exisiting
                if (typeof TARGET !== 'undefined') TARGET = event.target;
                // prevent middle click scroll
                if (mouseButton === 4) event.preventDefault();
            }
        }

        //-->Handles mousemove for frames; send message with position
        function handleFrameMousemove(event) {
            // on mouse button and no supression key
            if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {
                runtime.sendMessage({
                    subject: "gestureFrameMousemove",
                    data: {
                        screenX: event.screenX,
                        screenY: event.screenY
                    }
                });
                // prevent text selection
                if (mouseButton === 1) window.getSelection().removeAllRanges();
            }
        }

        //--> Handles mouseup for frames
        function handleFrameMouseup(event) {
            // only call on left, right and middle mouse click to terminate or reset gesture
            if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4) || (event.button === 2 && mouseButton === 2)))
                runtime.sendMessage({
                    subject: "gestureFrameMouseup",
                    data: {}
                });
        }

        // due to modul pattern: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
        return modul;
    })();

    //========⑤Setting===================
    const Ui = (function(){
        let modul = {};
        modul.init = function (){
            addStyle(CSS, 'MPmanageStyle');

            let node = document.createElement('div');
            node.id = 'MPsetting';
            node.innerHTML = menuHTML;
            document.body.appendChild(node);

            //#mg1
            q('#mg1')[0].innerHTML = gestureAndDragHTML;
            //#mg2
            q('#mg2')[0].innerHTML = makeFunsList();
            each(['gesture', 'text', 'link', 'image'],(item)=>{
                q('#mg2')[0].innerHTML += makeDefinedFunsList(item);
            });
            //#mg3
            q('#mg3')[0].innerHTML = aboutHTML;

            //addEventListener
            listen(q('#MPsetting')[0], 'click', click);
            each(q('#mg1 input[type=text], #mg2 span[name="alias"]'),item=>{
                listen(item, 'blur', updateConfigUi);
            });
            each(q('#MPsetting select, #MPsetting input[type=checkbox]'),item=>{
                listen(item, 'change', updateConfigUi);
            });
            //show functions,hide others
            q('li[name=mg2]')[0].click();
        };

        modul.closesetting = function (){
            q('body')[0].removeChild(q('#MPsetting')[0]);
        };

        modul.captureGesture = function(gestureStr, operation){
            try {
                if(operation === "recorddingGesture"){
                    q('#recorddingGesture')[0].textContent = gestureStr;
                    return;
                }
                if(operation !== "cancelGesture") q('[data-flag=captureGesture]')[0].value = gestureStr;
                document.body.removeChild(q('#MPMask')[0]);
                runtime.captureGesture = false;
                attr(q('#MPsetting')[0], "style", " ");
                let tmp = q('[data-flag=captureGesture]')[0];
                attr(tmp, "data-flag", " ");
                updateFns(tmp.parentElement);
            } catch(e) {
                // console.log(e);
            }
        };

        let
        fnLocal = {
            arg: {
                userDefine:{
                    description:{zh:['自定义功能代码'], en:['User Define Function Code']},
                    arg:['textarea']
                },
                openLinkText:{
                    description:{zh:['前台打开', '右侧打开'], en:['Load In Foreground', 'Open in Next Tab']},
                    arg:['selex:foreGround', 'selex:nextTab']
                },
                openLink:{
                    description:{zh:['前台打开', '右侧打开'], en:['Load In Foreground', 'Open in Next Tab']},
                    arg:['selex:foreGround', 'selex:nextTab']
                },
                openImgURL:{
                    description:{zh:['前台打开', '右侧打开'], en:['Load In Foreground', 'Open in Next Tab']},
                    arg:['selex:foreGround', 'selex:nextTab']
                },
                openImgLink:{
                    description:{zh:['前台打开', '右侧打开'], en:['Load In Foreground', 'Open in Next Tab']},
                    arg:['selex:foreGround', 'selex:nextTab']
                },
                searchText:{
                    description:{zh:['搜索引擎', '前台打开', '右侧打开'], en:['SearchingEnging', 'Load In Foreground', 'Open in Next Tab']},
                    arg:['select:searchEnging', 'selex:foreGround', 'selex:nextTab']
                },
                searchoropen:{
                    description:{zh:['搜索引擎', '后台打开', '右侧打开'], en:['SearchingEnging', 'Load In Foreground', 'Open in Next Tab']},
                    arg:['select:searchEnging', 'selex:foreGround', 'selex:nextTab']
                },
                searchImg:{
                    description:{zh:['图片搜索引擎', '前台打开', '右侧打开'], en:['Image SearchingEnging', 'Load In Foreground', 'Open in Next Tab']},
                    arg:['select:imgSearchEnging', 'selex:foreGround', 'selex:nextTab']
                }
            },
            FunsListTitle: {
                gesture: {zh:'手势',     en:'Gesture'},
                text:    {zh:'拖拽文本', en:'Drag Text'},
                link:    {zh:'拖拽链接', en:'Drag Link'},
                image:   {zh:'拖拽图片', en:'Drag Image'}
            },
            addFunction: {zh:'增加一个功能', en:'Add Function'}
        },
            CSS = `
        #MPsetting{z-index:999997!important;background:white!important;width:100%!important;height:100%!important;color:#032E58!important;font-family:"微软雅黑"!important;position:fixed!important;top:0!important;left:0!important;}
        #MPmenu *,
        .MPcontent *{border-radius:3px!important;font-size:16px!important;}
        #MPlogo svg{background:white!important;box-shadow:inset 0 0 25px 15px #A2B7D2!important;width:80px!important;height:100px!important;margin:0!important;padding:0!important;}
        #MPmenu{z-index:999999!important;height:100%!important;width:100px!important;background:#A2B7D2!important;color:white!important;text-align:center!important;}
        #MPmenu li{list-style-type:none!important;border-top:1px dashed white!important;margin:10px 15px!important;cursor:pointer;}
        .MPselected,#MPmenu li:hover{background:white!important;color:#A2B7D2!important;}
        #MPmenu li span{display:block!important;width:40px!important;height:40px!important;font-size:35px!important;font-weight:bold!important;padding:0 15px!important;}
        #MPmenu b{display:block!important;width:70px!important;text-align:center!important;margin-top:10px!important;}
        .MPcontent{height:94%!important;width:100%!important;overflow-y:scroll!important;position:absolute!important;left:100px!important;top:0!important;z-index:999998!important;padding:20px!important;}
        .MPcontent h1{display:block!important;width:800px!important;font-size:20px!important;float:left!important;top:0!important;left:90px!important;padding:3px 10px!important;margin:0 5px!important;border-left:5px solid #A2B7D2!important;background:#A2B7D259!important;}
        .MPcontent > li{list-style-type:none!important;width:800px!important;height:auto!important;padding:10px 5px 0px 5px!important;margin:5px 20px!important;float:left!important;border-bottom:1px dashed #00000020!important;}
        .MPcontent > li:hover{box-shadow:inset 1px 1px 1px 3px #A2B7D240!important;}
        #mg1 >li span:nth-child(2),#mg2>li>input{max-height:28px!important;float:right!important;}
        #mg1 input[type="text"],#mg1 select,#mg2 input[readonly="readonly"]{width:250px!important;height:26px!important;margin:0 10px!important;text-align:center!important;border:0!important;background:#0000000C!important;font-size:20px!important;}
        .MPcontent input[type="checkbox"]{width:0!important;}
        #FunsList{width:800px!important;border:0!important;overflow:hidden!important;}
        .FunsListHide{height: 34px!important;border: 0!important;margin: 0!important;padding: 0!important;}
        .FunsListShow{height:auto!important;}
        #FunsList>li{display:inline-block!important;width:300px!important;height:30px!important;margin:5px!important;text-align:left!important;}
        span.tag:before{color:white!important;background:#555555!important;margin:0!important;border:0!important;padding:3px!important;border-radius:4px 0 0 4px!important;font-size:14px!important;white-space:nowrap!important;font-weight:bold!important;}
        span.tag{color:white!important;margin:0!important;border:0!important;padding:1px 7px 3px 0!important;border-radius:4px!important;}

        #mg2 b{margin-left:30px;padding:0 20px;background:#00000000!important;}
        #mg2 div.fnArgument{<!--display:none;-->margin-left:0;margin-right:10px;padding-top:20px!important;height:auto;}<!--显示搜索引擎 去除空白-->
        #mg2 div.fnArgument textarea{width:100%;height:200px;}
        #mg2 div.fnArgument span{width:auto;height:auto;}
        #mg2 .yellow{background:#FFB400!important;}
        #mg2 .yellow:before{content:"${fnLocal.FunsListTitle.gesture[cfg.language]}";}
        #mg2 .blue:before{content:"${fnLocal.FunsListTitle.link[cfg.language]}";}
        #mg2 .blue{background:#1182C2!important;}
        #mg2 .green:before{content:"${fnLocal.FunsListTitle.text[cfg.language]}";}
        #mg2 .green{background:#4DC71F!important;}
        #mg2 .darkcyan:before{content:"${fnLocal.FunsListTitle.image[cfg.language]}";}
        #mg2 .darkcyan{background:#B10DC9!important;}
        #mg2 > li[data-type=gesture]>span:first-child{background:#FFB40030!important;color:#FFB400!important;}
        #mg2 > li[data-type=text]>span:first-child{background:#4DC71F30!important;color:#4DC71F!important;}
        #mg2 > li[data-type=link]>span:first-child{background:#1182C230!important;color:#1182C2!important;}
        #mg2 > li[data-type=image]>span:first-child{background:#B10DC930!important;color:#B10DC9!important;}
        #mg1 > li span:first-child,#mg2>li>span:first-child{text-align:left!important;font-size:16px!important;font-weight:bold!important;padding:2px 6px!important;width:auto!important;height:24px!important;float:left!important;border-left:5px solid!important;margin-right:20px!important;}
        #mg2>li>span{margin-bottom:10px!important;}
        #mg2>li>input {font-family: MParrow;}

        #mg2 div input[type=text],#mg2 div select{background:#0000000c;padding:5px;margin:10px 5px;border: 0;}
        #mg2 div input{width:80%;}
        #mg2 div select{width:15%;}
        #mg2 div label{margin-left:15px;margin-right:15px;}<!--margin:3px 0;-->

        #mg3 *{height: auto;font-size: 30px!important;text-decoration: none;font-weight: bolder;padding: 20px; color:#3A3B74!important}

        /*label 作为开关*/
        label.switchOn{background:#3A3B7420!important;display:inline-block!important;color:#3A3B74!important;font-weight:bolder!important;min-width:40px!important;height:24px!important;padding:2px 5px!important;border-left:15px solid #3A3B74!important;border-radius:5px!important;}
        label.switchOff{background:#33333370!important;display:inline-block!important;color:#333333a0!important;<!--text-decoration:line-through!important;-->min-width:40px!important;height:24px!important;padding:2px 5px!important;border-right:15px solid #333333!important;border-radius:5px!important;}
        input[type=checkbox].switch{width:0px!important;}

        #MPMask{z-index:9999999;position:fixed;top:0;left:0;}
        #recorddingGesture{position: fixed;width: 100%;top: 100%;margin-top: -50%;text-align: center;color: white;font-size: 40px;font-family: MParrow;word-wrap:break-word;}
      `,
            uiLocal = {
                //gesture
                gestureUi: {zh:'手势配置', en:'Gesture Config'},
                mouseButton: {zh:'手势按键', en:'Gesture mouse button'},
                leftButton: {zh:'左键', en:'Left Key'},
                middleButton: {zh:'中键', en:'MIddle Key'},
                rightButton: {zh:'右键', en:'Right Key'},
                mouseButtonTitle: {zh:'触发鼠标手势的按键', en:'The mouse button which will trigger the gesture.'},

                suppressionKey: {zh:'手势禁用键', en:'Gesture suppression key'},
                suppressionKeyTitle: {zh:'按下禁用键,暂时禁用手势', en:'Disables the mouse gesture if the key is pressed.'},
                distanceThreshold: {zh:'手势距离阈值', en:'Gesture distance threshold'},
                distanceThresholdTitle: {zh:'激活鼠标手势的最短距离', en:'The minimum mouse distance until the Gesture gets activated.'},
                distanceSensitivity: {zh:'手势灵敏度', en:'Gesture sensitivity'},
                distanceSensitivityTitle: {zh:'认定为新方向的最短距离。这也影响轨迹平滑度', en:'The minimum mouse distance until a new direction gets recognized. This will also impact the trace smoothness.'},
                Timeout: {zh:'手势超时', en:'Gesture timeout'},
                timeoutTitle: {zh:'鼠标不动指定时间后,取消手势', en:'Cancels the gesture after the mouse has not been moved for the specified time.'},
                directions: {zh:'手势方向数', en:'Gesture directions'},
                directionsTitle: {zh:'手势识别的方向个数', en:'Gesture diffrent directions.'},
                language: {zh:'语言', en:'Language'},
                languageTitle: {zh:'设定使用语言', en:'Set the language for using.'},
                //hint
                hintUi: {zh:'提示配置', en:'Hint Config'},
                background: {zh:'提示背景颜色', en:'Hint background'},
                backgroundTitle: {zh:'提示的文字的背景颜色', en:'Hint text background color'},
                fontSize: {zh:'提示字体', en:'Hint font size'},
                fontSizeTitle: {zh:'提示文字的字体大小,单位:"&quot;px"&quot;', en:'Hint text font size,unit:"&quot;px"&quot;'},
                lineColor: {zh:'轨迹颜色', en:'Track line color'},
                lineColorTitle: {zh:'显示轨迹的颜色,十六进制,可以使3/6/8位', en:'track line color, hex, 3/6/8 bit'},
                minLineWidth: {zh:'最小宽度', en:'Track minimum width'},
                minLineWidthTitle: {zh:'轨迹的最小宽度,单位:&quot;px"&quot;', en:'Track minimum width,unit:&quot;px"&quot;'},
                maxLineWidth: {zh:'最大宽度', en:'Track maximum width'},
                maxLineWidthTitle: {zh:'轨迹的最大宽度,单位:&quot;px"&quot;', en:'Track maximum width,unit:&quot;px&quot;'},
                lineGrowth: {zh:'增长速度', en:'Track growth speed'},
                lineGrowthTitle: {zh:'轨迹的增长速度,单位:&quot;px&quot;', en:'Track growth speed,unit:&quot;px&quot;'},
                funNotDefine: {zh:'未定义提示', en:'Gesture not found hint'},
                funNotDefineTitle: {zh:'手势或者功能未定义时的提示信息', en:'If gesture not found, hint this'},
                //drag
                dragSetting: {zh:'拖拽配置', en:'Drag Config'},
                linktextAslink: {zh:'链接优先', en:'Link priority'},
                linktextAslinkTitle: {zh:'链接文字识别为链接', en:'Text link drag as link'},
                dragInTextarea: {zh:'文本框拖拽', en:'Enable drag in textarea'},
                dragInTextareaTitle: {zh:'文本框中选中文字并且拖拽时候,使用拖拽的功能', en:'Enable drag in textarea or input'}
            },

            menuHTML = `
        <div id="MPmenu">
          <span id="MPlogo">
            <svg width="80px" height="100px" viewbox="0 0 200 200">
            <path d="M135 13 l13 13h-7v20h20v-7l13 13l-13 13v-7h-20v20h7l-13 13 l-13 -13h7v-20h-20v7l-13-13l13-13v7h20v-20h-7z" style="fill:#0074d9;stroke:none;"></path>
            <path d="M0 190L20 10c3,-8 8,-4 10,0L100 130L160 80c8,-8 17,-8 20,0L200 180c-2 20 -24 20 -30 0L160 120L110 163c-6 6 -19 10 -25 0L30 40L10 195c-3 5 -8 5 -10 0z" style="stroke:none;fill:#0074d9;"></path>
            </svg>
          </span>
          <li name="mg1">   <span>◧</span>  <b>Config</b>  </li>
          <li name="mg2">   <span>↯</span>   <b>Gesture</b> </li>
          <li name="mg3">   <span>❓</span>  <b>About</b>   </li>
          <li name="close"> <span>?</span>  <b>Close</b>   </li>
        </div>
        <div id="mg1" class="MPcontent">mg1</div>
        <div id="mg2" class="MPcontent">mg2</div>
        <div id="mg3" class="MPcontent">mg3</div>
      `,
            gestureAndDragHTML =
            //======gestureAndDragHTML======
            `
  <h1>${uiLocal.gestureUi[cfg.language]}</h1>
  <!-- 因为启用了左键作为拖拽,所以按钮选项要禁用
  <li>
    <span title="${uiLocal.mouseButtonTitle[cfg.language]}">${uiLocal.mouseButton[cfg.language]}</span>
    <span>
     <select name="mouseButton">
      <option value="0" ${sel(cfg.Gesture.mouseButton, 0)}>${uiLocal.leftButton[cfg.language]}</option>
      <option value="1" ${sel(cfg.Gesture.mouseButton, 1)}>${uiLocal.middleButton[cfg.language]}</option>
      <option value="2" ${sel(cfg.Gesture.mouseButton, 2)}>${uiLocal.rightButton[cfg.language]}</option>
     </select>
    </span>
  </li>
  -->
  <li>
    <span title="${uiLocal.suppressionKeyTitle[cfg.language]}">${uiLocal.suppressionKey[cfg.language]}</span>
    <span>
     <select name="suppressionKey">
      <option value="" ${sel(cfg.Gesture.suppressionKey, '')}>&nbsp;</option>
      <option value="altKey" ${sel(cfg.Gesture.suppressionKey, 'altKey')}>Alt</option>
      <option value="ctrlKey" ${sel(cfg.Gesture.suppressionKey, 'ctrlKey')}>Ctrl</option>
      <option value="shiftKey" ${sel(cfg.Gesture.suppressionKey, 'shiftKey')}>Shift</option>
     </select>
     </span>
  </li>
  <li>
    <span title="${uiLocal.distanceThresholdTitle[cfg.language]}">${uiLocal.distanceThreshold[cfg.language]}</span>
    <span>
      <input type="text" name="distanceThreshold" value="${cfg.Gesture.distanceThreshold}" data-mark="number">
      </span>
    </li>
  <li>
    <span title="${uiLocal.distanceSensitivityTitle[cfg.language]}">${uiLocal.distanceSensitivity[cfg.language]}</span>
    <span>
      <input type="text" name="distanceSensitivity" value="${cfg.Gesture.distanceSensitivity}"  data-mark="number">
    </span>
  </li>
  <li>
    <span title="${uiLocal.timeoutTitle[cfg.language]}">${uiLocal.Timeout[cfg.language]}</span>
    <span>
      <input type="text" name="Timeout" value="${cfg.Gesture.Timeout.duration}" data-mark="number">
    </span>
  </li>
  <li>
    <span title="${uiLocal.directionsTitle[cfg.language]}">${uiLocal.directions[cfg.language]}</span>
    <span>
      <select name="directions">
       <option value="4" ${sel(cfg.directions, 4)}> 4 </option>
       <option value="8" ${sel(cfg.directions, 8)}> 8 </option>
      </select>
    </span>
  </li>
  <li>
    <span title="${uiLocal.languageTitle[cfg.language]}">${uiLocal.language[cfg.language]}</span>
    <span>
     <select name="language">
      <option value="zh" ${sel(cfg.language, 'zh')}>中文</option>
      <option value="en" ${sel(cfg.language, 'en')}>English</option>
     </select>
    </span>
  </li>
  <h1>${uiLocal.hintUi[cfg.language]}</h1>
  <li>
    <span title="${uiLocal.backgroundTitle[cfg.language]}">${uiLocal.background[cfg.language]}</span>
    <span>
      <input type="text" name="background" value="${cfg.Hinter.background}" style="background:#${cfg.Hinter.background} !important;">
    </span>
  </li>
  <li>
    <span title="${uiLocal.fontSizeTitle[cfg.language]}">${uiLocal.fontSize[cfg.language]}</span>
    <span>
      <input type="text" name="fontSize" value="${cfg.Hinter.fontSize}" data-mark="number">
    </span>
  </li>
  <li>
    <span title="${uiLocal.lineColorTitle[cfg.language]}">${uiLocal.lineColor[cfg.language]}</span>
    <span>
      <input type="text" name="lineColor" value="${cfg.Hinter.lineColor}" style="background:#${cfg.Hinter.lineColor} !important;">
    </span>
  </li>
  <li>
    <span title="${uiLocal.minLineWidthTitle[cfg.language]}">${uiLocal.minLineWidth[cfg.language]}</span>
    <span>
      <input type="text" name="minLineWidth" value="${cfg.Hinter.minLineWidth}">
    </span>
  </li>
  <li>
    <span title="${uiLocal.maxLineWidthTitle[cfg.language]}">${uiLocal.maxLineWidth[cfg.language]}</span>
    <span>
      <input type="text" name="maxLineWidth" value="${cfg.Hinter.maxLineWidth}">
    </span>
  </li>
  <li>
    <span title="${uiLocal.lineGrowthTitle[cfg.language]}">${uiLocal.lineGrowth[cfg.language]}</span>
    <span>
      <input type="text" name="lineGrowth" value="${cfg.Hinter.lineGrowth}">
    </span>
  </li>
  <li>
    <span title="${uiLocal.funNotDefineTitle[cfg.language]}">${uiLocal.funNotDefine[cfg.language]}</span>
    <span>
      <input type="text" name="funNotDefine" value="${cfg.Hinter.funNotDefine}">
    </span>
  </li>

  <h1>${uiLocal.dragSetting[cfg.language]}</h1>
  <li>
    <span title="${uiLocal.linktextAslinkTitle[cfg.language]}">${uiLocal.linktextAslink[cfg.language]}</span>
    <span>
     <select name="linktextAslink">
      <option value="true" ${sel(cfg.Drag.linktextAslink, true)}>是</option>
      <option value="false" ${sel(cfg.Drag.linktextAslink, false)}>否</option>
     </select>
    </span>
  </li>
  <!--  使用抑制键代替
  <li>
    <span title="${uiLocal.dragInTextareaTitle[cfg.language]}">${uiLocal.dragInTextarea[cfg.language]}</span>
    <span>
      <input type="checkbox" id="dragInTextarea" name="dragInTextarea" checked="" class="switch">
      <label for="dragInTextarea" class="switchOn"></label>
    </span>
  </li>
  -->
      `,

            //=======gestureAndDragHTML End=========
            aboutHTML = `
        <pre style="font-size:1.2em !important;">
        About userDefine function:
        there are one argument(Object:mpData) provided in userDefine function.
        mpData is a object like this:
        {
          gesture:"646",         //gesture code of last mouse gesure
          link:{                 //optional, the target is link/image link...
            href: "https://www.baidu.com/",
            title: null, textContent: ""
          }
          target:{
            src: "https://www.baidu.com/img/baidu_jgylogo3.gif",   //target element arrtibute: src
            title: "到百度首页",  //target element arrtibute: title
            alt: "到百度首页",    //target element arrtibute: alt
            textContent: "",      //target element's text content
            nodeName: "IMG",      //target element's node name
            self:{}               //target element itself
          }
          textSelection:""
        }
        So, code in textarea shuold be <em>function body.</em>
        And, you can add some not frequently used function as "userDefine" function to MP™
        </pre>
        <a href="https://github.com/woolition/greasyforks/blob/master/mouseGesture/HY-MouseGesture.md" >(● ̄(エ) ̄●)づ <br>Click Me to More(点我看更多介绍)! </a>
        `,
            options = {
                imgSearchEnging: {// image searching
                    //默认:    "null",
                    "":   "",
                    Baidu:   "https://graph.baidu.com/details?isfromtusoupc=1&tn=pc&carousel=0&promotion_name=pc_image_shituindex&extUiData%5bisLogoShow%5d=1&image=U-R-L",
                    Google:  "https://www.google.com/searchbyimage?sbisrc=cr_1_5_2&image_url=U-R-L",
                    TinEye:  "https://www.tineye.com/search?url=U-R-L",
                    Yandex:  "https://yandex.com/images/search?rpt=imageview&url=U-R-L"
                },
                searchEnging: {// text searching
                    //默认:    "null",
                    "":   "",
                    Baidu:   "https://www.baidu.com/s?wd=",
                    Google:  "https://www.google.com/search?q=",
                    Bing:    "https://www.bing.com/search?q=",
                    Yahoo:   "https://search.yahoo.com/search?p=",
                    Wiki:    "https://en.wikipedia.org/w/index.php?search=",
                    Taobao:  "https://s.taobao.com/search?q=",
                    Amazon:  "https://www.amazon.com/s/&field-keywords=",
                    Sogou:   "https://www.sogou.com/web?query=",
                    s360:    "https://www.haosou.com/s?q="
                },
                foreGround: {
                    //默认:    "false",
                    前台打开:    "00",
                    后台打开:    "01",
                    当前打开:    "02",
                },
                nextTab: {
                    //默认:    "true",
                    右侧标签页:    "10",
                    最后标签页:    "11"
                }
            };


        function q(cssSelector){
            return document.querySelectorAll(cssSelector);
        }
        function attr(element,attributeName, attributeValue){
            try {
                if(attributeValue) element.setAttribute(attributeName, attributeValue);
                else return element.getAttribute(attributeName);
            } catch(e) {}
        }
        function each(elementCollect,func){
            try{
                Array.prototype.forEach.call(elementCollect, (item)=>{func(item);});
            }catch(e){}
        }
        function listen(element, eventType, func){
            element.addEventListener(eventType, func, false);
        }
        function sel(val1, val2){
            return val1 == val2 ? 'selected="selected"' : '';
        }
        function click(evt){
            function getName(evt){
                if(evt.target.getAttribute('name')){
                    return evt.target.getAttribute('name');
                }else {
                    if(evt.target.parentElement.getAttribute('name'))
                        return evt.target.parentElement.getAttribute('name');
                    else
                        return evt.target.parentElement.parentElement.getAttribute('name');
                }
            }
            let named = getName(evt);
            switch (named) {
                case 'mg1':
                case 'mg2':
                case 'mg3':
                    each(q('.MPcontent'),(item)=>{
                        attr(item, 'style', 'display:none;');
                    });
                    attr(q('#'+named)[0], 'style', 'display:block;');
                    each(q('#MPmenu li'),item=>{
                        attr(item, 'class', ' ');
                    });
                    attr(q('[name='+named+']')[0], 'class', 'MPselected');
                    break;
                case 'close':
                    q('body')[0].removeChild(q('#MPsetting')[0]);
                    break;
                case 'addFunction':
                    toggleFunsList();
                    break;
                case 'addFunctionLi':
                    clickToMakeEle();
                    break;
                case 'alias':
                    attr(evt.target, 'contentEditable', "true");
                    break;
                case 'toggleArgument':

                    if(evt.target.textContent === "▲"){
                        evt.target.textContent = "▼";
                        try{attr(evt.target.parentElement.lastChild,"style","display:none;");}
                        catch(e){}
                    }else {
                        evt.target.textContent = "▲";
                        try{attr(evt.target.parentElement.lastChild,"style","display:block;");}
                        catch(e){}
                    }
                    break;
                case 'clearGesture':
                case 'cancelGesture':
                    modul.captureGesture("", named);
                    break;
                default:
                    if(cfg.hasOwnProperty(attr(evt.target, 'data-mark')))
                        addMask();
                    break;
            }
        }
        function arg2html(argument, type, trk){
            let html ="",
                argu, i,rand, trackTxt, name,
                argValue = [],
                agrDetail = [],
                description,
                selectName;
            if(typeof argument === "object")
                argu = argument;
            else
                argu = JSON.parse(argument);
            trackTxt = trk || '';
            name = argu.name;

            html += `<span>${name}</span><span name="alias">${argu.alias ? argu.alias : local[type][name][cfg.language]}</span><b style="visibility:${argu.arg.length ? "visible" : "hidden"};" name="toggleArgument">▲</b><input type="text" name="${name}" value="${trackTxt}" data-mark="${type}" readonly="readonly"><br/><div class="fnArgument">`;
            if(argu.arg.length > 0){
                argValue = trackTxt ? argu.arg : [];
                agrDetail = fnLocal.arg[name].arg;
                description = fnLocal.arg[name].description[cfg.language];

                for(i in agrDetail){
                    rand = Math.floor(Math.random()*1000);
                    switch (agrDetail[i].slice(0,5)) {
                        case 'texta':
                            html += `<span><textarea>${mpUnescape(argValue[i])}</textarea><i></i></span>`;
                            break;
                        case 'input':
                            html += '<span><input type="text"><i></i></span>';
                            break;
                        case 'check':
                            html += `<span><input type="checkbox" id="${name + rand}" value=${argValue[i] || false} ${argValue[i] ? "checked" : ''} class="switch" name="fnCheckbox"><label for="${name + rand}" ${argValue[i] ? 'class="switchOn"' : 'class="switchOff"'}>${description[i]}</label></span>`;
                            break;
                        case 'selec':
                            selectName = agrDetail[i].split(':').pop();
                            html += `<span><input type="text" value=${argValue[i] || ''}><select name="fnSelect">`;
                            for (let k in options[selectName]){
                                html += `<option value=${options[selectName][k]} ${sel(argValue[i], options[selectName][k])}>${k}</option>`;
                            }
                            html += '</select></span>';
                            break;
                        case 'selex':
                            selectName = agrDetail[i].split(':').pop();
                            html += `<span><select name="fnSelect">`;
                            for (let k in options[selectName]){
                                html += `<option value=${options[selectName][k]} ${sel(argValue[i], options[selectName][k])}>${k}</option>`;
                            }
                            html += '</select></span>';
                            break;
                        default:
                            html = `<span style="visibility:hidden;"></span>`;
                            break;
                    }
                }
            }
            return html + "</div>";
        }
        function makeFunsList(){
            let color = ['yellow', 'green', 'blue', 'darkcyan'],
                html = '',
                arg = null;
            each(['gesture', 'text', 'link', 'image'], (type)=>{
                each(Object.keys(local[type]), (fnName)=>{
                    if(fnLocal.arg.hasOwnProperty(fnName))
                        arg = Object.assign({name:fnName},fnLocal.arg[fnName]);
                    else
                        arg = {name:fnName,arg:[]};
                    html += `<li  data-type="${type}" data-arg='${JSON.stringify(arg)}' title="${local[type][fnName][cfg.language]}" name="addFunctionLi">
                      <span class="tag ${color[['gesture', 'text', 'link', 'image'].indexOf(type)]}">
                        <!--<span>${fnLocal.FunsListTitle[type][cfg.language]}</span>-->
                        <!--<span>-->${fnName}<!--</span>-->
                      </span>
                    </li>`;
                });
            });
            html = `<fieldset id="FunsList" class="FunsListHide">
                <h1 name="addFunction">${fnLocal.addFunction[cfg.language]} ➕ </h1><br/>
                ${html}
              </fieldset>`;
            return html;
        }
        function makeDefinedFunsList(type){
            let html ='';
            each(Object.keys(cfg[type]), item=>{
                try {
                    html += `<li data-arg='${JSON.stringify(cfg[type][item])}' data-type='${type}'>${arg2html(cfg[type][item], type, item)}`;
                } catch(e) {}
            });
            return html;
        }
        function clickToMakeEle(){
            let tarEle = event.target.tagName === 'LI' ? event.target : (event.target.parentNode.tagName === "LI" ? event.target.parentNode : event.target.parentNode.parentNode);
            let ele = document.createElement('li');
            ele.setAttribute('data-arg', tarEle.dataset.arg);
            ele.setAttribute('data-type', tarEle.dataset.type);
            ele.innerHTML = arg2html(tarEle.dataset.arg, tarEle.dataset.type);
            document.getElementById('mg2').insertBefore(ele, document.querySelector(`#mg2>li`));
            listen(ele, 'change', formChange);
            listen(ele.childNodes[2].childNodes[0], 'blur', updateConfigUi);
            //函数列表收缩, 回滚到顶部
            toggleFunsList();
            document.documentElement.scrollTo(0, 0);
        }
        function updateFns(ele){
            // check Conflict
            if(Object.keys(cfg[ele.dataset.type]).indexOf(ele.childNodes[3].value) > -1){
                if(JSON.parse(ele.dataset.arg).name !== cfg[ele.dataset.type][ele.childNodes[3].value].name){
                    attr(ele, "style", "background:red!important;");
                    alert("Gesture Conflict (手势冲突) !!!");
                    return;
                }
            }
            // setting gesture not null
            if(JSON.parse(ele.dataset.arg).name === "setting" && !ele.childNodes[3].value){
                attr(ele, "style", "background:red!important;");
                alert("Setting Gesture Cannot Set Null (设置手势不能为空) !!!");
                return;
            }
            attr(ele, "style", " ");

            let typeObject = {};
            each(q(`#mg2>li[data-type=${ele.dataset.type}]`), element=>updateItem(element));
            function updateItem(item){
                let childrens, trk, argValue=[], name, dataArgObject, alias, argumentNodes;
                trk = item.childNodes[3].value;
                alias = item.childNodes[1].textContent;
                //if mouse track is not empty , update Fns
                if(trk !== ''){
                    childrens = item.childNodes[5].childNodes;
                    dataArgObject = JSON.parse(item.dataset.arg);
                    each(childrens, item=>{
                        if(item.firstElementChild.value && item.firstElementChild.value !== "undefined"){
                            // console.log(item.firstElementChild.nodeName);
                            // console.log('updateItem..');
                            if(item.firstElementChild.nodeName === "TEXTAREA")
                                argValue.push(mpEscape(item.firstElementChild.value));
                            else
                                argValue.push(item.firstElementChild.value);
                        } else{
                            argValue.push(' ');
                        }
                    });
                    typeObject[trk] = {name: dataArgObject.name, arg: argValue, alias:alias};
                }
            }
            // console.log(typeObject);
            cfg[ele.dataset.type] = typeObject;
            storage.set('cfg', cfg);
        }
        function updateConfigUi(e){
            let name = attr(e.target, 'name');
            switch (name) {
                case 'mouseButton':
                case 'suppressionKey':
                    cfg.Gesture[name] = e.target.value;
                    break;
                case 'distanceThreshold':
                    cfg.Gesture[name] = parseInt(e.target.value);
                    break;
                case 'distanceSensitivity':
                    cfg.Gesture[name] = parseInt(e.target.value);
                    break;
                case 'Timeout':
                    cfg.Gesture[name].duration = parseInt(e.target.value);
                    break;
                case 'directions':
                case 'language':
                    cfg[name] = e.target.value;
                    break;
                case 'background':
                case 'lineColor':
                    cfg.Hinter[name] = e.target.value;
                    attr(e.target, 'style', `background: #${e.target.value} !important;`);
                    break;
                case 'fontSize':
                case 'minLineWidth':
                case 'maxLineWidth':
                case 'lineGrowth':
                    cfg.Hinter[name] = parseFloat(parseFloat(e.target.value).toFixed(2));
                    break;
                case 'funNotDefine':
                    cfg.Hinter[name] = e.target.value;
                    break;
                case 'linktextAslink':
                case 'dragInTextarea':
                    cfg.Drag[name] = e.target.checked;
                    onOff(e, e.target.checked);
                    break;
                default:
                    if(name === "alias")
                        updateFns(e.target.parentElement);
                    else if(name === "fnCheckbox" || name==="fnSelect"){
                        formChange();
                    }
                    return;
            }
            storage.set('cfg', cfg);
        }
        function formChange(){
            if(event.target.type === 'checkbox'){
                event.target.value = event.target.checked;
                onOff(event, event.target.checked);
                updateFns(event.target.parentElement.parentElement.parentElement);
            }
            if(event.target.tagName === 'SELECT'){
                //event.target.previousElementSibling.value = event.target.value;
                //if( event.target.previousElementSibling.tagName === "INPUT") {
                if( event.target.previousElementSibling !== null) {//前input元素显示值 判断前元素
                    event.target.previousElementSibling.value = event.target.value;
                }
                updateFns(event.target.parentElement.parentElement.parentElement);
            }
        }
        function onOff(e, check) {
            if (check) {
                attr(e.target.nextElementSibling, 'class', 'switchOn');
            } else {
                attr(e.target.nextElementSibling, 'class', 'switchOff');
            }
        }
        function addMask(){
            let
            w=window.innerWidth,
                h=window.innerHeight,
                px = 0.1*w,
                string=`
        <svg height="${h}" width="${w}" style="background:#00000080">
        <path  id="record" d="
      M${50},     ${50+px}   v-${px}  h${px}
      M${w-px-50},${50}      h${px}   v${px}
      M${w-50},   ${h-px-50} v${px}   h-${px}
      M${50+px},  ${h-50}    h-${px}  v-${px}"
      style="stroke:#fff;stroke-width:${w/50};fill:none;"></path>
      <text name="clearGesture" x="100" y="${h-100}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">Clear</text>
      <text name="A" x="${w-w/2}" y="${h-h/2}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">A</text>
      <text name="cancelGesture" x="${w-100-w/6}" y="${h-100}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">Cancle</text>
      </svg>`;
            let mask = document.createElement('div');
            mask.id = "MPMask";
            mask.innerHTML = string + '<div id="recorddingGesture"></div>';
            document.body.appendChild(mask);
            each(q('text[name=clearGesture], text[name=cancelGesture]'), item=>listen(item,"click",click));

            attr(q('#MPsetting')[0], "style", "z-index:9999998 !important;");
            attr(event.target, "data-flag", "captureGesture");
            runtime.captureGesture = true;
        }
        function toggleFunsList(){
            let a = q('#FunsList')[0];
            if(attr(a, 'class') === "FunsListHide"){
                attr(a, 'class', 'FunsListShow');
            }else{
                attr(a, 'class', 'FunsListHide');
            }
        }


        return modul;
    })();

    //========⑥Run===================

    //this addStyle is better than GM_addStyle,but not working in CSP tabs
    // function addStyle(cssStr,id='MPStyle'){
    //   try {
    //     let node = document.createElement('style');
    //     node.id = id;
    //     node.textContent = cssStr;
    //     document.querySelector(':root').appendChild(node);
    //   } catch(e){}
    // }
    function addStyle(cssStr,id='MPStyle'){
        GM_addStyle(cssStr);
    }
    addStyle(`
    @font-face {
    font-family: 'MParrow';
    src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAQdAAoAAAAABPAAAQAAAAAAAAAAAAAAAAAAAAAECAAAABVPUy8yAAABYAAAAEQAAABgUc1dNGNtYXAAAAHEAAAARgAAAGAAcgFDZ2x5ZgAAAiAAAADwAAABNKukdSxoZWFkAAAA9AAAADQAAAA2DKcEFmhoZWEAAAEoAAAAHQAAACQEKQIaaG10eAAAAaQAAAAfAAAAJBGtAZVsb2NhAAACDAAAABQAAAAUATIBfm1heHAAAAFIAAAAFQAAACAACwAKbmFtZQAAAxAAAADnAAABe0DXvWtwb3N0AAAD+AAAABAAAAAgAAMAAXjaY2BkYGAA4gfLE97F89t8ZeBkYgCBq07amiD6mu+MRAaB/3cZXzFuAnI5GMDSAEgbC5142mNgZGBgYgACPSApwCDA+IqBkQEVcAIAGeEBSQAAAHjaY2BkYGDgBEIQzQAlkQAAAjsAFgAAAHjaY2Bm/MY4gYGVgYPRhzGNgYHBHUp/ZZBkaGFgYGJg5WSAAUYGJBCQ5poCpAwZLBkf/H/AoMeEpIaRAcpjAAAVNgmoeNpjYmBgYPzCYAbE3lBagImBQQzM/srgA6IBjAwITgB42i2KywmAQBQD57l+e9gCvAoieLd/7ShmnwZCmDBA4WslaLlMkdyzekdv0LFzSuaNQ9Kj+/ebUfNf0iv2YfA7Mb+pBQmvAAAAAAAAABQAJgA6AEwAXgByAIYAmnjaVY8hT8NAGIa/N0tzLJlgbY4LYmI0zekvTTmBuHomcGT9DXMkpD8Bwd+AhIo1wa8CVYfF4DCgm8wV7m6Gqc+8eZ7nI9AlRejwSCdERvAkYqHEQxljarv6zWIau0sEuv79xAtewy4tjJLpPH2q2rZqvtH3GAc6YiWaswlroQfPKLsaVzYe93ZXu90pneML94ElWRuWS/nhILO7qt2uG/K+M7f5OWxQsBJcLAtc9P04YLHeOu2xL1McJayMAtlx74W34YngW7n25tCe5VLoIp/nuAnxzz4eMwrO/zzDScZGG2xK393V74G7q/8AczlNtXjadY7BasJAEIb/mKgVSumh3ucBoiQetHjpod6K4MlLi7CROSzEBDaB0EfoC/hEvoLv0990G0Rwhtn99p9/hwHwiCMCXCLAsD0v0eP94DnEuNMjjDruY8rOHw/ofqcziEZUnvDhuccfn55D+v/1CC8d9/GFb88DPOO83hjnykbetuoqWxaSTpPkmmWlez1k6mQeyyxJF7HYwtbW5OI0V1OpHzHBGhsYOGaJBrJ7/TlhiS2USgVLtYAg5WoJ854uWLGzZx2QtR7BHDHPGbspFi1b/rGoWQY5347OnGU4UW82mfwCMzM4HQB42mNgZkAGjAxoAAAAjgAFSExQRAEARkMJAAAAUGF3J1oAAAAA) format('woff');
    }
    #MPcanvas{position:fixed;top:0;left:0;z-index:10000000;}
    #MPtips{all:initial!important;position:fixed!important;z-index:9999996!important;top:50%!important;left:50%!important;transform:translate(-50%,-50%)!important;font-family:MParrow,"Arial",sans-serif!important;color:white!important;white-space:nowrap!important;line-height:normal!important;text-shadow:1px 1px 5px rgba(0,0,0,0.8)!important;text-align:center!important;padding:25px 20px 20px 20px!important;border-radius:5px!important;font-weight:bold!important; }
  `);
    //===========update any time=========
    GM_addValueChangeListener('cfg', ()=>{
        GestureHandler.applySettings(cfg);
        Hinter.applySettings(cfg);
    });
    //when close a tab, save it's url, in order to reopen it: reopenTab
    window.addEventListener('unload', function() {
        //GM_setValue('latestTab', window.location.href);
    }, false);
    //used in func: closeOtherTabs
    if(!GM_getValue('closeAll','')) GM_setValue('closeAll', Date());
    GM_addValueChangeListener('closeAll',function(name, old_value, new_value, remote){if(remote)window.close();});
    //===========update any time end=========

    GestureHandler.applySettings(cfg);
    Hinter.applySettings(cfg);
    GestureHandler.enable();
    Hinter.enable();

    //========Remind===================

    //叠加显示
    var wrapEle = document.createElement('div');
    wrapEle.id = "wrap";
    wrapEle.setAttribute('style', '' +
                         'position:fixed;' +
                         'right:0px;' +
                         'top:0px;' +
                         'width:300px;' +//最大宽度
                         //'padding:40px;' +
                         'background-color:rgba(255,255,255,0)!important;' +
                         'z-index:2147483647!important;' +//显示最顶层
                         '');
    document.body.appendChild(wrapEle);//元素加入body

    function showclipboard(text) {
        const wrapDiv = document.getElementById("wrap");
        var div = document.createElement('div');
        div.setAttribute('style', '' +
                         'display:none!important;' +//去掉直接显示
                         'left:0px;' +
                         'top:0px;' +
                         'margin-left:auto;' +//table块靠右显示
                         //'position:absolute!important;' +
                         'font-size:4px!important;' +
                         'overflow:auto!important;' +
                         'background-color:rgba(255,255,255,0.8)!important;' +
                         'font-family:sans-serif,Arial!important;' +
                         'font-weight:normal!important;' +
                         'text-align:left!important;' +//左对齐
                         'color:#000!important;' +
                         'padding:0.5em 1em!important;' +
                         'border-radius:3px!important;' +
                         'border:1px solid #ccc!important;' +
                         //'max-width:350px!important;' +
                         'max-height:1216px!important;' +
                         'z-index:2147483647!important;' +
                         '');

        div.innerHTML = text;
        div.style.display = 'table';// 换行显示结果
        let fc = wrapDiv.firstElementChild
        if (fc) {
            wrapDiv.insertBefore(div,fc)
        } else {
            wrapDiv.appendChild(div);
        }
        setTimeout(() => {
            div.parentNode.removeChild(div);
        },6000)

    }



})();