Greasy Fork is available in English.

MouseGesture--HY

HY's mouse gesture script,supports ringt-key draw track functions and left-key drag functions.Drag target can be [Text] & [Links] & [Image] Customizenable → Right click to draw ⇄(left,right) to setting

目前為 2017-12-16 提交的版本,檢視 最新版本

// ==UserScript==
// @name         MouseGesture--HY
// @name:zh-CN     鼠标手势--就是这么拽!
// @namespace      https://greasyfork.org/users/104201
// @description    HY's mouse gesture script,supports ringt-key draw track functions and left-key drag functions.Drag target can be [Text] & [Links] & [Image]  Customizenable → Right click to draw ⇄(left,right) to setting
// @description:zh-CN  鼠标手势脚本,就是这么拽:支持右键轨迹手势和左键拖拽功能.可以拖拽[文本],[链接]和[图片],支持自定义设置:鼠标画➡⬅(右左)路径,进入设置
// @version      1.6
// @include      *
// @noframes
// @run-at       document-end
// @grant        GM_openInTab
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_setClipboard
// @grant        GM_download
// @grant        GM_addValueChangeListener
// @grant        GM_notification
// @grant        window.close
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// Thanks to: Peer Zeng's script:  https://greasyfork.org/zh-CN/scripts/4776-my-mouse-gestures [no License] [for right click gesture handle]
// Thanks to: crxMouse Chrome™ Gestures [chrome crxID:jlgkpaicikihijadgifklkbpdajbkhjo] [for: drag processing]
// Thanks to: Robbendebiene's project Gesturefy [https://github.com/Robbendebiene/Gesturefy] [for canvas line style]
// Thanks to: Jim Lin's userscript 有道划词翻译 [https://greasyfork.org/zh-CN/scripts/15844] [License MIT][for 划词翻译]
// ==/UserScript==
/* jshint esversion: 6 */

const MouseGesture = (function() {
  let arrowCss = `@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:9999999;}
    #MPtips{all:initial!important;position:fixed!important;z-index:9999998!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; }
    `;
  GM_addStyle(arrowCss);
  let dObj = {};// the Object Element being draged
  let x, y, startX, startY, screenX, screenY, canvas, tips, ctx,
  direction = '', track = '';
  //_*:  default cfg values
  let _cfg = {
    t2n:{           // t2n: track <==> function name
      "2":  {name:"toTop",    arg:[]},
      "8":  {name:"toBottom", arg:[]},
      "4":  {name:"back",     arg:[]},
      "6":  {name:"forward",  arg:[]},
      "86": {name:"close",    arg:[]},
      "42": {name:"reopenTab",arg:[]},
      "64": {name:"setting",  arg:[]}
    },
    dt2n: {         //dt2n: dragText <==> function name
      "8":  {name:"copyText", arg:[]},
      "6":  {
              name:"searchText",
              arg:["http://www.baidu.com/s?wd=", true, true]
            }
    },
    dl2n: {         //dl2n: drag link <==> function name
      "6": {name:"openLink",   arg:[]},
      "8": {name:"copyLink",   arg:[]}
    },
    di2n: {         //li2n: drag image <==> function
      "8": {name:"saveImg",    arg:[]},
      "6": {
              name:"searchImg",
              arg:['https://image.baidu.com/n/pc_search?queryImageUrl=%URL&uptype=urlsearch', true, true]
            },
      "2": {name:"copyImgURL", arg:[]},
      "4": {name:"selectImg",  arg:[]}
    },
    directions:        4,              // 4 or 8 directions
    minLineWidth:      1,               //canvas setting
    lineGrowth:        0.6,
    maxLineWidth:      10,
    lineColor:         '00AAA0',

    fontSize:          50,             //tips font size
    tipsBackground:    "00000055",     //div background
    funNotDefine:      "  (◔ ‸◔)?",  //function not define tips

    language:          1,              //language 0:Chinese 1:English
    sensitivity:       10,             // minLength
    translateTo:      'zh-CHS',
    translateTimeout: 5,
    zoom:             2,

    dragType:         "",
    dragtext:         true,
    draginput:        true,
    draglink:         true,
    dragimage:        true,
    imgfirst:         false,
    imgfirstcheck:    true,
    setdragurl:       true
  };
  //function name <==> tips
  let fn = {
    t2n: {
      stopLoading:     ['停止加载',         'StopLoading'],
      reload:          ['刷新',             'Refresh'],
      reloadNoCache:   ['清缓存刷新',       'Refresh Without Cache'],
      close:           ['关闭',             'Close'],
      back:            ['后退',             'Back'],
      forward:         ['前进',             'Forward'],
      toTop:           ['到顶部',           'Scroll to Top'],
      toBottom:        ['到底部',           'Scroll to Bottom'],
      reopenTab:       ['打开最近关闭窗口', 'Reopen Latest Closed Window'],
      setting:         ['设置',             'Settings'],
      URLLevelUp:      ['网址向上一层',     'URL hierarchy up'],
      cloneTab:        ['克隆标签页',       'Duplicate This Tab'],
      openBlankTab:    ['打开空白页',       'Open New Blank Tab'],
      translate:       ['翻译网页',         'Translate This Page'],
      fkVip:           ['破解VIP视频',      'Crack to Watch VIP Video'],
      closeOtherTabs:  ['关闭其他标签',     'Close Other Tabs'],
      translateSelect: ['开启划词翻译',     'Turn on Select And Translate'],
      //开发者功能
      contentEditable: ['元素内容可编辑',   'Element Content Editable']
    },
    dt2n: {
      searchText:      ['搜索选中文本',     'Search Selected Text'],
      copyText:        ['复制选中文本',     'Copy Selected Text']
    },
    dl2n: {
      openLink:        ['打开链接',         'Open Link'],
      copyLink:        ['复制链接',         'Copy Link'],
      copyLinkText:    ['复制链接文字',     'Copy Link Text']
    },
    di2n: {
      saveImg:         ['保存图片',         'Save Image'],
      searchImg:       ['搜索图片',         'Search Image'],
      copyImage:       ['复制图片',         'Copy Image to ClickBoard'],
      copyImgURL:      ['复制图片链接',     'Copy ImageURL'],
      openImgNewTab:   ['新标签打开图片',   'Open Image in New Tab'],
      image2DataURL:   ['复制图片为DataURL','Copy Image as DataURL'],
      selectImg:       ['选中图片',         'Select This Image']
    }
  };
  //function name <==> function declaration  ==> execute it
  let Fn = {
    stopLoading: function() {
      window.stop();
    },
    reload: function(arr) {
      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() {
      window.open('about:blank');
    },

    //use MicrosoftTranslator to translate the page
    translate: function(arr) {

      if (typeof Microsoft === 'undefined' || typeof Microsoft.Translator === 'undefined') {
        let d = document.createElement('div');
        d.id = "MicrosoftTranslatorWidget";
        d.style.cssText = 'visibility:hidden;';
        d.setAttribute('class', 'Lignt');
        let s = document.createElement('script');
        s.type = 'text/javascript';
        s.charset = 'UTF-8';
        s.src = ((location && location.href && location.href.indexOf('https') == 0) ? 'https://ssl.microsofttranslator.com' : 'http://www.microsofttranslator.com') + '/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**&ctf=False&ui=true&settings=Manual&from=';
        let p = document.getElementsByTagName('head')[0] || document.documentElement;
        p.insertBefore(s, p.firstChild);
        document.body.appendChild(d);
      }
      let onComplete, onProgress, onError;
      onError = function(){
        GM_notification({
          title: 'MouseGesture:',
          text: cfg.language ? "出了问题,无法完成翻译" : "Oops,Something wrong Hapend...",
          timoeout: 2000
        });
        tips.parentNode.removeChild(tips);
      };
      onComplete = function() {
        tips.parentNode.removeChild(tips);
      };
      onProgress = function() {
        document.documentElement.appendChild(tips);
        tips.innerHTML = cfg.language ? "翻译中..." : "Translating...";
      };
      let doTranslate = function() {
        if (typeof Microsoft === 'undefined' || typeof Microsoft.Translator === 'undefined') return;
        clearInterval(loadTranslatorTimer);
        Microsoft.Translator.Widget.Translate('', arr[0], onProgress, onError, onComplete, () => {}, (cfg.translateTimeout) * 1000);
      };
      loadTranslatorTimer = setInterval(doTranslate, 200);
      setTimeout(() => clearTimeout(loadTranslatorTimer), (cfg.translateTimeout) * 1000);
    },
    fkVip: function() {
      GM_openInTab((cfg.vipApi)+location.href, {active:true});
    },
    closeOtherTabs: function() {
      GM_setValue('closeAll', Date());
    },
    translateSelect: function() {
      window.document.body.addEventListener('mouseup', translate, false);
      var context = new AudioContext();
      function translate(e) {
        var previous = document.querySelector('.youdaoPopup');
        if (previous) {
          document.body.removeChild(previous);
        }
        var selectObj = document.getSelection();
        if (selectObj.anchorNode.nodeType == 3) {
          var word = selectObj.toString();
          if (word == '') {
            return;
          }
          word = word.replace('-\n', '');
          word = word.replace('\n', ' ');
          var ts = new Date().getTime();
          var x = e.clientX;
          var y = e.clientY;
          translate(word, ts);
        }
        function popup(x, y, result) {
          var youdaoWindow = document.createElement('div');
          youdaoWindow.classList.toggle('youdaoPopup');
          var dict = JSON.parse(result);
          var query = dict.query;
          var errorCode = dict.errorCode;
          if (dict.basic) {
            word();
          } else {
            sentence();
          }
          youdaoWindow.style.cssText = `z-index:999999;display:block;position:fixed;color:black;text-align:left;word-wrap:break-word;background:lightBlue;border-radius:5px;box-shadow:0 0 5px 0;opacity:1;width:200px;left:${x+10}px;padding:5px`;
          if (x + 200 + 10 >= window.innerWidth) {
            youdaoWindow.style.left = parseInt(youdaoWindow.style.left) - 200 + 'px';
          }
          if (y + youdaoWindow.offsetHeight + 10 >= window.innerHeight) {
            youdaoWindow.style.bottom = '20px';
          } else {
            youdaoWindow.style.top = y + 10 + 'px';
          }
          document.body.appendChild(youdaoWindow);
          function word() {
            var basic = dict.basic;
            var header = document.createElement('p');
            var span = document.createElement('span');
            span.innerHTML = query;
            header.appendChild(span);
            var phonetic = basic.phonetic;
            if (phonetic) {
              var phoneticNode = document.createElement('span');
              phoneticNode.innerHTML = '[' + phonetic + ']';
              phoneticNode.style.cursor = 'pointer';
              header.appendChild(phoneticNode);
              phoneticNode.addEventListener('mouseup', function(e) {
                e.stopPropagation();
              }, false);
              var soundUrl = 'https://dict.youdao.com/dictvoice?type=2&audio={}'.replace('{}', query);
              var promise = new Promise(function() {
                GM_xmlhttpRequest({
                  method: 'GET',
                  url: soundUrl,
                  responseType: 'arraybuffer',
                  onload: function(res) {
                    try {
                      context.decodeAudioData(res.response, function(buffer) {
                        phoneticNode.addEventListener('mouseup', function() {
                          var source = context.createBufferSource();
                          source.buffer = buffer;
                          source.connect(context.destination);
                          source.start(0);
                        }, false);
                        header.appendChild(document.createTextNode('✓'));
                      });
                    } catch (e) {}
                  }
                });
              });
              promise.then();
            }
            header.style.color = 'darkBlue';
            header.style.margin = '0';
            header.style.padding = '0';
            span.style.color = 'black';
            youdaoWindow.appendChild(header);
            var hr = document.createElement('hr');
            hr.style.margin = '0';
            hr.style.padding = '0';
            youdaoWindow.appendChild(hr);
            var ul = document.createElement('ul');
            ul.style.margin = '0';
            ul.style.padding = '0';
            basic.explains.map(function(trans) {
              var li = document.createElement('li');
              li.style.listStyle = 'none';
              li.style.margin = '0';
              li.style.padding = '0';
              li.appendChild(document.createTextNode(trans));
              ul.appendChild(li);
            });
            youdaoWindow.appendChild(ul);
          }
          function sentence() {
            var ul = document.createElement('ul');
            ul.style.margin = '0';
            ul.style.padding = '0';
            dict.translation.map(function(trans) {
              var li = document.createElement('li');
              li.style.listStyle = 'none';
              li.style.margin = '0';
              li.style.padding = '0';
              li.appendChild(document.createTextNode(trans));
              ul.appendChild(li);
            });
            youdaoWindow.appendChild(ul);
          }
        }
        function translate(word, ts) {
          var reqUrl = 'http://fanyi.youdao.com/openapi.do?type=data&doctype=json&version=1.1&relatedUrl=' +
            escape('http://fanyi.youdao.com/#') +
            '&keyfrom=fanyiweb&key=null&translate=on' +
            '&q={}'.replace('{}', word) +
            '&ts={}'.replace('{}', ts);
          GM_xmlhttpRequest({
            method: 'GET',
            url: reqUrl,
            onload: function(res) {
              popup(x, y, res.response);
            }
          });
        }
      }
    },
    contentEditable: function(){
      console.log('x:'+startX+' y:'+startY);
      document.elementFromPoint(startX, startY).setAttribute('contenteditable', 'true');
      document.elementFromPoint(startX, startY).setAttribute('data-mp', '1');
    },
    /*
    //not torking
    zoomIn: function(){
       setTimeout(zoomer, 200);
      function zoomer(evt){
        let a, b,isZoom = true;
        a = document.elementFromPoint(evt.clientX,evt.clientY).style.zoom=cfg.zoom;
        a.setAttribute('data-zoom', 'true');
        [].every.forEach(document.querySelectorAll('*[data-zoom=true]'), function(item){
          if (item !== a) item.style.zoom = null;
        });
      }
    },*/

    searchText: function(arr) {
      //get text
      let txt = window.getSelection().toString();
      txt = encodeURIComponent(txt);
      //get search enging
      openURL = arr[0] + txt;
      GM_openInTab(openURL, {
        active: arr[1],
        insert: arr[2],
        setParent: true   //makes the browser re-focus the current tab on close.
      });
    },
    copyText: function() {
      GM_setClipboard(dObj.text, "text");
    },
    openLink: function() {
      //TamperMonkey
      GM_openInTab(dObj.link, {
        active: true
      });
    },
    copyLink: function() {
      GM_setClipboard(dObj.link, "text");
    },
    copyLinkText: function() {
      GM_setClipboard(dObj.text, "text");
    },
    saveImg: function() {
      //TamperMonkey
      let arr = dObj.img.split('/');
      let name = arr[arr.length - 1];
      GM_download(dObj.img, name);
      //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(event,arr) {
      //TamperMonkey
      GM_openInTab(arr[0].replace(/%URL/, dObj.img), {
        active: arr[1],
        insert: arr[2],
        setParent: true   //not working
      });
    },
    selectImg: function() {
      // it may not working on some browsers [develping standard]
      //TamperMonkey
      document.execCommand('selectAll');
      let sel = document.getSelection();
      sel.collapse(dObj.target, 0);
      sel.modify("extend", "forward", "character");
    },
    //not working:
    copyImage: function(e) {
      let canvas = canvasDrawTheImage(e);
      // get image as blob
      canvas.canvas.toBlob((blob) => {
        GM_setClipboard(blob, {
          type: canvas.type,
          mimetype: canvas.mime
        });
      }, canvas.mime);
    },
    image2DataURL: function(e) {
      //canvas绘制图片,由于浏览器的安全考虑:
      //如果在使用canvas绘图的过程中,使用到了外域的图片资源,那么在toDataURL()时会抛出安全异常:
      let canvas = canvasDrawTheImage(e).canvas;
      let dataURL = canvas.toDataURL();
      GM_setClipboard(dataURL, "text");
    },
    copyImgURL: function() {
      //TamperMonkey
      GM_setClipboard(dObj.img, "text");
    },
    openImgNewTab: function() {
      //TamperMonkey
      GM_openInTab(dObj.img, {
        active: true
      });
    },
    setting: function() {
      if (document.getElementById('HYetting')) {
        return;
      }
      makeSettingUi();
    }
  };
  let flag = {
    actionType:'',
    isDrag:    false,      //if drag ,isDrag = true
    isPress:   false,     //if mouse right key is press,ispress = true
    hascanvas: false,   //if document has <canvas> hascanvas = true
    isZoom:    false       //zoom mode
  };
  //============ supportive functions ==> used by Fn{}'s function
  //check if string is an url
  function isURL(string) {
    try {
      new URL(string);
    } catch (e) {
      return false;
    }
    return true;
  }
  function isObject (item) {
    return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
  }
  /**
   * deep merge two objects into a new one
   * from https://stackoverflow.com/a/37164538/3771196
   **/
  function mergeDeep (target, source) {
    let output = Object.assign({}, target);
    if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
      if (!(key in target))
        Object.assign(output, { [key]: source[key] });
      else
        output[key] = mergeDeep(target[key], source[key]);
      }
      else Object.assign(output, { [key]: source[key] });
    });
    }
    return output;
  }
  let cfg = mergeDeep(_cfg,GM_getValue('cfg', _cfg));
  //return: {canvas:canvas,type:fileType,mime:mimeType}
  function canvasDrawTheImage(e) {
    // let img = e.target,
    let img = dObj.target,
      fileType = img.src.split('/').pop().split('.').pop().toLowerCase(),
      mimeType = 'image/' + fileType,
      canvas = document.createElement('canvas');
    canvas.width = getNaturalSize(img).width;
    canvas.height = getNaturalSize(img).height;
    canvas.getContext('2d', {
      alpha: true
    }).drawImage(img, 0, 0);
    return {
      canvas: canvas,
      type: fileType,
      mime: mimeType
    };
  }
  // function canvasDrawTheImage(e) {
  //   // let img = e.target,
  //   var img=new Image;
  //   img.onload=function(){
  //     var canvas=document.createElement("canvas");
  //     canvas.width = getNaturalSize(img).width;
  //     canvas.height = getNaturalSize(img).height;
  //     var g=canvas.getContext("2d",{alpha:true});
  //     g.drawImage(img,0,0);
  //     console.log(canvas.toDataURL());
  //   };
  //   img.crossOrigin="anonymous"; //关键
  //   img.src=dObj.img;
  //   return {
  //     canvas: canvas,
  //     type: 'png',
  //     // type: fileType,
  //     mime: 'image/png'
  //     // mime: mimeType
  //   };
  // }
  // get image natural width and height
  function getNaturalSize(ele) {
    let i, w, h;
    if (typeof ele.naturalWidth == 'undefined') { // IE 6/7/8
      i = new Image();
      i.src = ele.src;
      w = i.width;
      h = i.height;
    } else { // HTML5 browsers
      w = ele.naturalWidth;
      h = ele.naturalHeight;
    }
    return {
      width: w,
      height: h
    };
  }
  //============ function for all
  //show Tips
  function showTips(){
    if (cfg[cfg.dragType][track] !== undefined) {
      tips.innerHTML = track + '<br/>' + fn[cfg.dragType][cfg[cfg.dragType][track].name][cfg.language];
    } else {
      tips.innerHTML = track + '<br/>' + (cfg.funNotDefine);
    }
  }
  function drawTrack(e){
    if (flag.hascanvas) {
      ctx.lineWidth = Math.min(cfg.maxLineWidth, ctx.lineWidth += cfg.lineGrowth);
      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
      ctx.closePath();
    }
  }
  function getDirection(cx, cy){
    /*=================
    |                 |
    | 1↖   2↑   3↗ |
    |                 |
    | 4←    5    6→ |
    |                 |
    | 7↙   8↓   9↘ |
    |                 |
    |=================*/
    let d, t;
    if(cfg.directions === 4){   //4 directions
      if (dx < dy) {
        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;
  }
  //draw track & show tips
  function tracer(e) {
   // const tracer = function(e) {
    let cx = e.clientX,
      cy = e.clientY;
      dx = Math.abs(cx - x),
      dy = Math.abs(cy - y);
      distance = dx * dx + dy * dy;
    if (distance < cfg.sensitivity * cfg.sensitivity) {
      return;
    }
    //if mouse right key is press and document has no <canvas>,then creaet <canvas> and append it
    //到里面才添加元素是为了避免 鼠标一按下,还没有移动就已经图层了
    //这个图层有两方面作用,①画出轨迹 ②支持拖拽功能, 因为作用②,所以不能移到 drawTrack 函数里面
    if (flag.isPress && !flag.hascanvas) addCanvas(e);
    let direction = getDirection(cx,cy);
    if (track.charAt(track.length - 1) !== direction) {
      track += direction;
      showTips();     //show action tips
    }
    drawTrack(e);      //draw track on canvas
    // update (x,y)
    x = cx;
    y = cy;
  }
  //<canvas> & tips<div> is ready, when mousemove or drag, append to show track & tips
  function addCanvas(e) {
    document.documentElement.appendChild(tips);     //append tips <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(cfg.lineColor.length>6)canvas.style.opacity = parseInt(cfg.lineColor.slice(6),16)/255;
    ctx.lineWidth = cfg.minLineWidth;
    ctx.strokeStyle = '#' + cfg.lineColor.slice(0,6); //like delicious link color//line color
    flag.hascanvas = true;
  }
  //remove <canvas> and tips<div>,set flags to false
  function reset() {
    if (flag.hascanvas) {
      document.documentElement.removeChild(canvas);
      document.documentElement.removeChild(tips);
    }
    flag.isPress = false;
    flag.hascanvas = false;
  }
  function createCanvaTips(){
    //create <canvas>
    canvas = document.createElement("canvas");
    canvas.id = 'MPcanvas';
    ctx = canvas.getContext("2d");
    //create tips<div>
    tips = document.createElement('div');
    tips.id = 'MPtips';
    tips.style.cssText = `background:#${cfg.tipsBackground} !important;  font-size: ${cfg.fontSize}px !important;`;
  }
  createCanvaTips();

  //=========== event processing
  //right click ==> gesture
  window.addEventListener('mousedown', function(e) {
    // 3 : mouse.right ; 1:mouse.left
    if (e.which === 3) {
      startX = x = e.clientX;
      startY = y = e.clientY;
      track = "";
      flag.isPress = true;
      // flag.actionType = "common";
      cfg.dragType = "t2n";
      window.addEventListener('mousemove', tracer, false);
    }
  }, false);
  window.addEventListener('contextmenu', function(e) {
    reset();
    window.removeEventListener('mousemove', tracer, false);
    if (track !== "") {
      e.preventDefault();
      if (cfg.t2n.hasOwnProperty(track)) {
        Fn[cfg.t2n[track].name](cfg.t2n[track].arg);
      }
    }
  }, false);

  //left click ==> drag
  window.addEventListener('dragstart', function(e) {
    startX = x = e.clientX;
    startY = y = e.clientY;
    track = '';
    flag.isPress = true;
    flag.isDrag = true;
    flag.actionType = "drag";
    processDrag(e);
    window.addEventListener('drag', tracer, false);
    //避免释放鼠标时候,坐标跑到(0,0) window.allowDrop
    this.allowDrop = function(event) {
      event.preventDefault();
    };
    tips.addEventListener("dragover", allowDrop, false);
    canvas.addEventListener("dragover", allowDrop, false);
  }, false);
  window.addEventListener('dragend', function(e) {
    window.removeEventListener('drag', tracer, false);
    tips.removeEventListener("dragover", allowDrop, false);
    canvas.removeEventListener("dragover", allowDrop, false);
    reset();
    isDrag = false;
    if (track !== "" && cfg[cfg.dragType].hasOwnProperty(track)) {
      // dragType + track => function
      Fn[cfg[cfg.dragType][track].name](event,cfg[cfg.dragType][track].arg);
    }
  }, false);

  function processDrag(e) {
    //========这部分借鉴 crxMouse Chrome™ Gestures, crxID:jlgkpaicikihijadgifklkbpdajbkhjo===========
    dObj.target = e.target;
    let nodetype = e.target.nodeType;
    //confirm dragType
    if (nodetype === 3) {
      let isLink = e.target.parentNode.href;
      if (cfg.dragtext && !isLink) {
        // cfg.dragType = "text";
        cfg.dragType = "dt2n";
      } else if (isLink) { //use regular express to match?
        e = e.target.parentNode;
        cfg.dragType = "dl2n";
        // cfg.dragType = "link";
      }
    }
    if (nodetype === 1) {
      if (e.target.value && cfg.dragtext && cfg.draginput) {
        cfg.dragType = "dt2n";
        // cfg.dragType = "text";
      } else if (e.target.href) {
        if (window.getSelection().toString() == "" || e.target.textContent.length > window.getSelection().toString().lenght) {
          if (cfg.draglink) {
            cfg.dragType = "dl2n";
            // cfg.dragType = "link";
          }
        } else {
          if (cfg.dragtext) {
            cfg.dragType = "dt2n";
            // cfg.dragType = "text";
          }
        }
        if (!cfg.dragtext && cfg.draglink) {
          cfg.dragType = "dl2n";
          // cfg.dragType = "link";
        }
      } else if (e.target.src) {
        if (e.target.parentNode.href) {
          if (cfg.dragimage && (e[cfg.imgfirst + "Key"] || cfg.imgfirstcheck)) {
            cfg.dragType = "di2n";
            // cfg.dragType = "image";
          } else if (cfg.draglink) {
            cfg.dragType = "dl2n";
            // cfg.dragType = "link";
            e = e.target.parentNode;
          }

        } else if (cfg.dragimage) {
          cfg.dragType = "di2n";
          // cfg.dragType = "image";
        }
      }

    }


    if (!cfg.dragType) {
      flag.isDrag = false;
      return;
    }
    dObj.text = window.getSelection().toString() || e.target.innerHTML;
    dObj.link = e.href || e.target.href;
    dObj.img = e.target.src;
    if (cfg.setdragurl && cfg.dragType == "dt2n") {
      var tolink;
      if (dObj.text.indexOf("http://") != 0 && dObj.text.indexOf("https://") != 0 && dObj.text.indexOf("ftp://") != 0 && dObj.text.indexOf("rtsp://") != 0 && dObj.text.indexOf("mms://") != 0 && dObj.text.indexOf("chrome-extension://") != 0 && dObj.text.indexOf("chrome://") != 0) {
        tolink = "http://" + dObj.text;
      } else {
        tolink = dObj.text;
      }
      var urlreg = /^((chrome|chrome-extension|ftp|http(s)?):\/\/)([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
      if (urlreg.test(tolink)) {
        cfg.dragType = "dl2n";
        dObj.link = tolink;
      }
    }
    //========== crxID:jlgkpaicikihijadgifklkbpdajbkhjo END===========
    return dObj;
  }

  //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 all tabs MG's config
  if(!GM_getValue('configChanged','')) GM_setValue('configChanged', Date());
  GM_addValueChangeListener('configChanged',function(name, old_value, new_value, remote){
    if(remote) cfg = GM_getValue('cfg', _cfg);
    createCanvaTips();
  });

  //========== Setting UI
  let makeSettingUi = function(){
    let CSS = `
      #MPsetting{z-index:999997!important;background:white!important;width:100%!important;height:100%!important;font-family:"微软雅黑"!important;position:fixed!important;top:0!important;left:0!important;}
      #MPmenu *,
      .MPcontent *{border-radius:5px!important;font-size:16px!important;}
      #MPlogo{background:white!important;box-shadow:inset 0 0 25px 15px yellowgreen!important;width:80px!important;height:80px!important;padding:0 10px 30px 10px!important;display:block!important;font-size:80px!important;color:cyan!important;text-shadow:6px 5px 15px black!important;}
      #MPmenu{z-index:999999!important;height:100%!important;width:100px!important;background:yellowgreen!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;}
      .MPselected{box-shadow:inset 2px 2px 1px 4px rgba(16,12,12,0.6)!important;}
      #MPmenu li:hover{background:#05FDE7!important;color:#FF841D!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,
      #FunsList legend{display:block!important;width:800px!important;font-size:30px!important;float:left!important;top:0!important;left:90px!important;padding:5px 12px!important;margin:0 10px!important;border-left:5px solid yellowgreen!important;background:#9acd3259!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 #9acd32de!important;}
      .MPcontent >li span:nth-child(2){background:#00000010!important;text-align:left!important;padding:4px 10px!important;height:20px!important;width:auto!important;float:left!important;}
      .MPcontent >li span:nth-child(3){max-height:28px!important;float:right!important;}
      .MPcontent input[type="text"]{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;}
      .MPcontent select{width:100%!important;height:100%!important;}
      #FunsList{width:800px!important;border:0!important;overflow:hidden!important;}
      .FunsListHide{height:40px!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{margin:2px 7px!important;padding:0!important;border:0!important;font-size:11px!important;white-space:nowrap!important;font-weight:bold!important;}
      span.tag span:first-child{color:white!important;background:#555!important;margin:0!important;padding:3px 3px 3px 7px!important;border:0!important;border-radius:4px 0 0 4px!important;}
      span.tag span:last-child{color:white!important;margin:0!important;border:0!important;padding:3px 7px 3px 3px!important;border-radius:0 4px 4px 0!important;}
      span.tag.yellow span:last-child{background:#FFB400!important;}
      span.tag.blue span:last-child{background:#1182C2!important;}
      span.tag.green span:last-child{background:#4DC71F!important;}
      span.tag.darkcyan span:last-child{background:#4DB798!important;}
      .MPcontent > li[data-type=t2n] span:first-child{background:#FFB40030!important;color:#FFB400!important;}
      .MPcontent > li[data-type=dt2n] span:first-child{background:#4DC71F30!important;color:#4DC71F!important;}
      .MPcontent > li[data-type=dl2n] span:first-child{background:#1182C230!important;color:#1182C2!important;}
      .MPcontent > li[data-type=di2n] span:first-child{background:#4DB79830!important;color:#4DB798!important;}
      .MPcontent > li span:first-child{text-align:left!important;font-size:18px!important;font-weight:bold!important;padding:2px 10px!important;width:auto!important;height:24px!important;float:left!important;border-left:10px solid!important;margin-right:20px!important;}
      #mg2>li>span{margin-bottom:10px!important;}
      #mg2>li>span:nth-child(3)>input {font-family: MParrow;}
      #mg2 > li span:nth-child(5),
      .MPcontent > li span:nth-child(6),
      .MPcontent > li span:nth-child(7){max-height:30px!important;margin-bottom:100px!important;white-space:nowrap!important;}
      .MPcontent > li span:nth-child(5) select{width:auto!important;height:26px!important;}

      /*label 作为开关*/
      label.switchOn{background:#4dc71f20!important;display:inline-block!important;color:#4DC71F!important;font-weight:bolder!important;min-width:40px!important;height:24px!important;padding:2px 5px!important;border-left:15px solid #4DC71F!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;}
    `;
    let span = '', isOn = '', isChecked = '', settingDiv, settingParent,
      //菜单栏HTML
      txt = `<div id="MPmenu"><span id="MPlogo">☈</span><li data-target="mg1"><span>◧</span><b>Config</b></li><li data-target="mg2"><span>↯</span><b>Gesture</b></li><li data-target="mg3"><span>❓</span><b>About</b></li><li id="close"><span>?</span><b>Close</b></li></div>`;
    let setting = {
      mg1Start:         {eletype: '1',  id: 'mg1'},
      mg1title1:        {eletype: '2'},
      maxLineWidth:     {type: 'input',    name: 'maxLineWidth',     more: 'num'},
      lineGrowth:       {type: 'input',    name: 'lineGrowth',       more: 'num'},
      fontSize:         {type: 'input',    name: 'fontSize',         more: ''},
      lineColor:        {type: 'input',    name: 'lineColor',        more: 'color'},
      funNotDefine:     {type: 'input',    name: 'funNotDefine',     more: ''},
      language:         {type: 'input',    name: 'language',         more: 'num'},
      sensitivity:      {type: 'input',    name: 'sensitivity',      more: 'num'},
      tipsBackground:   {type: 'input',    name: 'tipsBackground',   more: 'color'},
      translateTimeout: {type: 'input',    name: 'translateTimeout', more: ''},
      mg1title2:        {eletype: '2'},
      dragtext:         {type: 'checkbox', name: 'dragtext',         more: ''},
      draginput:        {type: 'checkbox', name: 'draginput',        more: ''},
      draglink:         {type: 'checkbox', name: 'draglink',         more: ''},
      dragimage:        {type: 'checkbox', name: 'dragimage',        more: ''},
      //imgfirst:       {imgfirstcheck: {type: 'checkbox', name: 'imgfirstcheck', more: ''},
      setdragurl:       {type: 'checkbox', name: 'setdragurl',       more: ''},
      mg1end:           {eletype: '3'}
    };
    let selectobjs = {
      //languages
      translateTo: {"Afrikaans":"af","Haitian Creole":"ht","Querétaro Otomi":"otq","Arabic":"ar","Hebrew":"he","Romanian":"ro","Bangla":"bn","Hindi":"hi","Russian":"ru","Bosnian (Latin)":"bs-Latn","Hmong Daw":"mww","Samoan":"sm","Bulgarian":"bg","Hungarian":"hu","Serbian (Cyrillic)":"sr-Cyrl","Cantonese (Traditional)":"yue","Indonesian":"id","Serbian (Latin)":"sr-Latn","Catalan":"ca","Italian":"it","Slovak":"sk","Chinese Simplified":"zh-CHS","Japanese":"ja","Slovenian":"sl","Chinese Traditional":"zh-CHT","Kiswahili":"sw","Spanish":"es","Croatian":"hr","Klingon":"tlh","Swedish":"sv","Czech":"cs","Korean":"ko","Tahitian":"ty","Danish":"da","Latvian":"lv","Tamil":"ta","Dutch":"nl","Lithuanian":"lt","Thai":"th","English":"en","Malagasy":"mg","Tongan":"to","Estonian":"et","Malay":"ms","Turkish":"tr","Fijian":"fj","Maltese":"mt","Ukrainian":"uk","Filipino":"fil","Norwegian Bokmål":"no","Urdu":"ur","Finnish":"fi","Persian":"fa","Vietnamese":"vi","French":"fr","Polish":"pl","Welsh":"cy","German":"de","Portuguese":"pt","Yucatec Maya":"yua","Greek":"el"},
      imgSearchEnging: {    // image searching
        BaiduImage:  "https://image.baidu.com/n/pc_search?queryImageUrl=%URL&uptype=urlsearch",
        GoogleImage: "https://www.google.com/searchbyimage?image_url=%URL",
        TinEye:      "http://www.tineye.com/search?url=%URL"
      },
      searchEnging: {       // text searching
        google:  "http://www.google.com/search?q=",
        baidu:   "http://www.baidu.com/s?wd=",
        yandex:  "http://www.yandex.com/yandsearch?text=",
        Bing:    "http://www.bing.com/search?q=",
        yahoo:   "http://search.yahoo.com/search?p=",
        wiki:    "http://en.wikipedia.org/w/index.php?search=",
        taobao:  "http://s.taobao.com/search?q=",
        amazon:  "http://www.amazon.com/s/&field-keywords=",
        sogou:   "https://www.sogou.com/web?query=",
        s360:    "http://www.haosou.com/s?q="
      },
      vipAPI:{
        疯狂:    "http://goudidiao.com/?url=",
        噗噗:    "http://pupudy.com/play?make=url&id="
      }
    };
    let local = {
      arg: {
        searchText:{
          description:[['搜索引擎', '后台打开', '右边插入'], ['SearchingEnging', 'Load In Background', 'Insert After Current Tab']],
          arg:['select:searchEnging', 'checkbox', 'checkbox']
        },
        searchImg:{
          description:[['图片搜索引擎', '后台打开', '右边插入'], ['Image SearchingEnging', 'Load In Background', 'Insert After Current Tab']],
          arg:['select:imgSearchEnging', 'checkbox', 'checkbox']
        },
        translate:{
          description:[['目标语言'], ['Translate To']],
          arg:['select:translateTo']
        },
        fkVip:{
          description:[['视频解析接口'], ['Videos Parser API']],
          arg:['select:vipAPI']
        }
      },
      FunsListTitle: {
        t2n: ['手势', 'Gesture'],
        dt2n: ['拖拽文本', 'Drag Text'],
        dl2n: ['拖拽链接', 'Drag Link'],
        di2n: ['拖拽图片', 'Drag Image']
      },
      tips: ['说明', 'Description'],
      addFunction: ['增加一个功能', 'Add Function'],
      //setting prefix: SET => SET + functionName + Item
      mg1title1ITEM:       ['界面',                           'UI'],
      maxLineWidthITEM:    ['轨迹宽度',                       'Line Width'],
      maxLineWidthDESC:    ['鼠标轨迹最大宽度,单位"px"',      'Mouse Track Max. Line Width'],
      lineGrowthITEM:      ['轨迹增长',                       'Line Grow'],
      lineGrowthDESC:      ['轨迹增长速度,单位"px"',          'Track Growing Speed, Unit "px"'],
      fontSizeITEM:        ['提示字体大小',                   'Tips Font Size'],
      fontSizeDESC:        ['功能提示字体的大小,单位"px"',    'Function Tips Font Size, Unit "px"'],
      lineColorITEM:       ['轨迹颜色',                       'Line Color'],
      lineColorDESC:       ['3|6|8位16进制值,如 0f0 ,00ff00, 00ff0080','3|6|8 Hexadecimal Value, eg. 0f0,00ff00,00ff0080'],
      funNotDefineITEM:    ['未定义提示',                     'Not Define Tips'],
      funNotDefineDESC:    ['手势或者功能未定义时的提示信息', 'Undefined Function Tips'],
      languageITEM:        ['语言',                           'Language'],
      languageDESC:        ['0 表示中文 1 表示英语',          '0 for Chinese, 1 for English'],
      sensitivityITEM:     ['识别距离',                       'Sensitivigy'],
      sensitivityDESC:     ['方向变化计算距离',               'Min Direction Change Distance'],
      tipsBackgroundITEM:  ['提示文字背景颜色',               'Tis Background Color'],
      tipsBackgroundDESC:  ['提示文字的背景颜色',             'Tips Background Color'],
      vipApiITEM:          ['破解视频接口',                   'Parse Video API'],
      vipApiDESC:          ['VIP视频及杰解析接口',            'VIP Videos Parser API'],
      translateTimeoutITEM:['等待时间',                       'Timeout'],
      translateTimeoutDESC:['翻译等待时间,超时作废',          'Translation Timeout'],
      mg1title2ITEM:       ['设定',                           'Setting'],
      dragtextITEM:        ['启用拖拽文字',                   'Enable Drag Text'],
      dragtextDESC:        ['选中文字并且拖拽时候的功能',     'Enable Drag Text'],
      draginputITEM:       ['启用拖拽文本框文字',             'Enable Drag Text-in-Inputbox'],
      draginputDESC:       ['文本框中选中文字并且拖拽时候,使用拖拽的功能','Enable Drag Text-in-Inputbox'],
      draglinkITEM:        ['启用拖拽链接',                   'Enable Drag Link'],
      draglinkDESC:        ['拖拽链接时候的功能',             'Enable Drag Link'],
      dragimageITEM:       ['启用拖拽图片',                   'Enable Drag Image'],
      dragimageDESC:       ['拖拽图片时候的功能',             'Enable Drag Image'],
      imgfirstcheckITEM:   ['图片链接识别为图片',             'Drag ImageLink as Drag Image'],
      imgfirstcheckDESC:   ['拖拽图片链接时候,识别为拖拽图片的功能','Drag Image-Link, Treat as Drag Image'],
      setdragurlITEM:      ['拖拽文本链接',                   'Drag LinkText as Drag Link'],
      setdragurlDESC:      ['拖拽文本为链接时候,识别为拖拽链接','Drag Link-Text, Treat as Drag Link']
    };
    let arg2html = function(argument, type, trk){
      let html ="", argu, i,rand, trackTxt, name, argValue = [], agrDetail = [], description;
      typeof argument === "object" ? argu = argument : argu = JSON.parse(argument);
      trackTxt = trk || '';
      name = argu.name;
      html += `<span>${name}</span><span>${fn[type][name][cfg.language]}</span><span><input type="text" name="${name}" value="${trackTxt}" data-mark="${type}"></span><br/>`;
      if(argu.arg.length > 0){
        argValue = trackTxt ? argu.arg : [];
        agrDetail = local.arg[name].arg;
        description = local.arg[name].description[cfg.language];
        for(i in agrDetail){
          rand = Math.floor(Math.random()*1000);
          switch (agrDetail[i].slice(0,5)) {
            case 'input':
              html += '<span><input type="text"></span>';
              break;
            case 'check':
              html += `<span><input type="checkbox" id="${name + rand}" value=${argValue[i] || false} ${argValue[i] ? "checked" : ''} class="switch"><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>`;
              for (let k in selectobjs[selectName]){
                html += `<option value=${selectobjs[selectName][k]}>${k}</option>`;
              }
              html += '</select></span>';
              break;
            default:
               html = `<span style="visibility:hidden;"><span></span></span>`;
              break;
          }
        }
      }
      return html;
    },
    makeFunsList = function(){
      let html = '',i=0,j,hasArgument,item2,arg,color=['yellow', 'green', 'blue', 'darkcyan'];
      ['t2n','dt2n','dl2n','di2n'].forEach((item1) => {
        for( item2 in fn[item1]){
          local.arg.hasOwnProperty(item2) ? arg = Object.assign({name:item2},local.arg[item2]) : arg = {name:item2,arg:[]};
          html +=`
          <li  data-arg='${JSON.stringify(arg)}' data-type="${item1}" title="${fn[item1][item2][cfg.language]}"><span class="tag ${color[i]}"><span>${local.FunsListTitle[item1][cfg.language]}</span><span>${item2}</span></span></li>
          `;
        }
        i += 1;
      });
      html = `<fieldset id="FunsList" class="FunsListHide"><h1>${local.addFunction[cfg.language]} ➕ </h1><br/>${html}</fieldset>`;

      return html;
    },
    makeDefinedFunsList = function(type){
      let i, html ='';
       for(i in cfg[type]){
        html += `<li data-arg='${JSON.stringify(cfg[type][i])}' data-type='${type}'>${arg2html(cfg[type][i], type, i)}`;
      }
       return html;
    },
    clickToMakeEle = function(){
      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`));
      try {
        if(ele.children[4]) ele.children[4].children[1].addEventListener('change', formChange, false);
        if(ele.children[5]) {
          ele.children[5].firstElementChild.addEventListener('change', formChange, false);
          ele.children[5].firstElementChild.addEventListener('change', onOff, false);
        }
        if(ele.children[6]) {
          ele.children[6].firstElementChild.addEventListener('change', formChange, false);
          ele.children[6].firstElementChild.addEventListener('change', onOff, false);
        }
      } catch(e) {
      }
      //轨迹框 失去焦点 更新设置
      ele.children[2].firstElementChild.addEventListener('blur', updateFns, false);
      ele.children[2].firstElementChild.addEventListener('keyup', function(event) {
        event.target.value = letter2arrow(event.target.value);
      }, false);
      //函数列表收缩, 回滚到顶部
      toggleFunsList();
      document.documentElement.scrollTo(0, 0);
    },
    updateFns = function(){
      var typeObject = {},
      tarEle = event.target;
      tarEle.tagName === "LI" ? null : (tarEle.parentNode.tagName === "LI" ? tarEle = tarEle.parentNode : tarEle = tarEle.parentNode.parentNode);
      [].forEach.call(document.querySelectorAll(`#mg2>li[data-type=${tarEle.dataset.type}]`), function(element, index) {
        updateItem(element);
      });
      function updateItem(item){
        let childrens, trk, argValue=[], name, dataArgObject;
        trk = item.children[2].firstElementChild.value;
        //if mouse track is not empty , update Fns
        if(trk !== ''){
          childrens = item.children;
          dataArgObject = JSON.parse(item.dataset.arg);
          if(childrens[4]) {
            if(childrens[4].firstElementChild.value && childrens[4].firstElementChild.value !== "undefined"){
              argValue[0] = childrens[4].firstElementChild.value;
            } else{
              argValue[0] = '';
            }
          }
          if(childrens[5]) {
            if(childrens[5].firstElementChild.value && childrens[5].firstElementChild.value !== "undefined"){
              argValue[1] = childrens[5].firstElementChild.value;
            } else{
              argValue[1] = '';
            }
          }
          if(childrens[6]) {
            if(childrens[6].firstElementChild.value && childrens[6].firstElementChild.value !== "undefined"){
              argValue[2] = childrens[6].firstElementChild.value;
            } else{
              argValue[2] = '';
            }
          }
          typeObject[trk] = {name: dataArgObject.name, arg: argValue};
        }

      }
      cfg[tarEle.dataset.type] = typeObject;
      GM_setValue('cfg', cfg);
      GM_setValue('configChanged', Date());
    },
    updateCfgUsr = function(e){
      switch (e.target.dataset.mark) {
        case 'color':
          cfg[e.target.name] = e.target.value;
          e.target.setAttribute('style', `background: #${e.target.value} !important;`);
          break;
        case 'num':
          let b;
          switch (e.target.name) {
            case 'language':
              b = (e.target.value == 1 || e.target.value == 0) ? e.target.value : cfg[e.target.name];
              break;
            case 'sensitivity':
            case 'fontSize':
              b = parseInt(e.target.value);
              break;
            default:
              b = parseFloat(parseFloat(e.target.value).toFixed(2));
              break;
          }
          cfg[e.target.name] = b;
          break;
        case 'select':
        case 'normal':
          cfg[e.target.name] = e.target.value;
          break;
        default:
          // cfg[e.target.id] = updateFns(`input[data-mark="${e.target.dataset.mark}"]`);
          break;
      }
      GM_setValue('cfg', cfg);
      GM_setValue('configChanged', Date());
    },
    formChange = function(){
      if(event.target.type === 'checkbox'){
        event.target.value = event.target.checked;
        updateFns();
      }
      if(event.target.tagName === 'SELECT'){
        event.target.previousElementSibling.value = event.target.value;
        updateFns();
      }
    },
    letter2arrow = function(str){
      return str.replace(/[^uUdDlLrR4682]/g, '').replace(/[lL]/g, '4').replace(/[rR]/g, '6').replace(/[dD]/g, '8').replace(/[uU]/g, '2');
    },
    onOff = function(e) {
      cfg[e.target.id] = e.target.checked;
      if (cfg[e.target.id]) {
        e.target.nextElementSibling.setAttribute('class', 'switchOn');
      } else {
        e.target.nextElementSibling.setAttribute('class', 'switchOff');
      }
      //GM***
    },
    toggleFunsList = function(){
      let a = document.getElementById('FunsList');
      if(a.getAttribute('class') === "FunsListHide"){
        a.setAttribute('class', 'FunsListShow');
      }else{
        a.setAttribute('class', 'FunsListHide');
      }
    },
    selected = function(e) {
      let tar;
      if (e.target.tagName === "LI") {
        tar = e.target;
      } else {
        tar = e.target.parentNode;
      }
      [].forEach.call(document.querySelectorAll('#MPmenu li'), function(item) {
        item.setAttribute('class', '');
      });
      tar.setAttribute('class', 'MPselected');
      [].forEach.call(document.querySelectorAll('.MPcontent'), function(item) {
        item.style.display = "none";
      });
      document.getElementById(tar.dataset.target).setAttribute('style', 'display:block;');
    };

    GM_addStyle(CSS);
    //#mg1 config
    for (let i in setting) {
      if (setting[i].eletype) {
        switch (setting[i].eletype) {
          case '1':
            txt += `<div id="${setting[i].id}" class="MPcontent">`;
            break;
          case '2':
            txt += `<h1>${local[i+'ITEM'][cfg.language]}</h1>`;
            break;
          case 1:
            txt += `<div id="${setting[i].id}" class="MPcontent">`;
            break;
          default:
            txt += `</div>`;
            break;
        }
      } else {
        if (setting[i].type === 'input') {
          if (setting[i].more === 'color') {
            span = `<input type="text" name="${setting[i].name}" value="${cfg[setting[i].name]}" style="background:#${cfg[setting[i].name]};"  data-mark="color">`;
          } else if (setting[i].more === 'num') {
            span = `<input type="text" name="${setting[i].name}" value="${cfg[setting[i].name]}" data-mark="num">`;
          } else {
            span = `<input type="text" name="${setting[i].name}" value="${cfg[setting[i].name]}" data-mark="normal">`;
          }
        } else {
          isChecked = cfg[setting[i].name] ? 'checked' : '';
          isOn = cfg[setting[i].name] ? 'class="switchOn"' : 'class="switchOff"';
          span = `<input type="checkbox" id="${setting[i].name}" name="${setting[i].name}" ${isChecked} class="switch"><label for="${setting[i].name}" ${isOn}></label>`;

        }
        txt += `<li><span>${local[i+'ITEM'][cfg.language]}</span><span title='${local[i+'DESC'][cfg.language]}'>${local.tips[cfg.language]}</span><span>${span}</span></li>`;
      }
    }

    //#mg2 Defined FunsList htms Gesture/Darg funcs
    txt +='<div id="mg2" class="MPcontent">';
    txt += makeFunsList();    //#mg2 FunsList html
    txt += makeDefinedFunsList('t2n');
    txt += makeDefinedFunsList('dt2n');
    txt += makeDefinedFunsList('dl2n');
    txt += makeDefinedFunsList('di2n');
    txt += '</div>';  //#mg2 end
    //#mg3
    txt += '<div id="mg3" class="MPcontent"><a href="https://github.com/woolition/greasyforks/blob/master/mouseGesture/HY-MouseGesture.md" style="display:block;width: 90%;height: auto;font-family:MParrow;font-size: 40px!important;text-decoration: none;font-weight: bolder;padding: 30px 30px; color:yellowgreen;">手势输入:<br>u or U: 2<br>r or R: 6<br> d or D: 8<br> l or L: 4<br><br>(● ̄(エ) ̄●)づ <br>点我看更多介绍! </a></div>';

    settingDiv = document.createElement('div');
    settingDiv.id = "MPsetting";
    settingDiv.innerHTML = txt;
    settingParent = document.body || document.documentElement;
    settingParent.appendChild(settingDiv);

    [document.querySelectorAll('#MPmenu li')[0],document.querySelectorAll('#MPmenu li')[1],document.querySelectorAll('#MPmenu li')[2]].forEach(function(item) {
      item.addEventListener('click', selected, false);
    });
    [].forEach.call(document.querySelectorAll('#mg1 input[type=text]'), function(item) {
      item.addEventListener('blur', updateCfgUsr, false);
    });
    [].forEach.call(document.querySelectorAll('#mg1 input[type=checkbox], #mg2 input[type=checkbox]'), function(item) {
      item.addEventListener('change', onOff, false);
      if(item.parentNode.parentNode.parentNode.id === 'mg1'){
        item.addEventListener('change',updateCfgUsr,false);
      }
    });
    [].forEach.call(document.querySelectorAll('#FunsList li'),(item)=>{
      item.addEventListener('click', clickToMakeEle, false);
    });
    [].forEach.call(document.querySelectorAll('#mg2 input[type=text]'), function(item) {
      item.addEventListener('blur', updateFns, false);
    });
    [].forEach.call(document.querySelectorAll('#mg2 select, #mg2 input[type=checkbox]'), function(item) {
      item.addEventListener('change', formChange, false);
    });
    [].forEach.call(document.querySelectorAll('#mg2>li'), function(item) {
      item.children[2].firstElementChild.addEventListener('keyup', function(event) {
        let a = event.target.value;
        a = letter2arrow(a);
        if(a.charAt(a.length -1) === a.charAt(a.length-2)) a = a.slice(0,-1);
        event.target.value = a;
      }, false);
    });
    //init
    [].forEach.call(document.querySelectorAll('.MPcontent'), function(item) {
      item.style.display = "none";
    });
    document.querySelector('#FunsList h1').addEventListener('click', toggleFunsList, false);
    document.getElementById('mg1').style.display = 'block';
    document.getElementById('close').addEventListener('click', function(e) {
      try {
        document.documentElement.removeChild(document.getElementById("MPsetting"));
      } catch(event) {
        document.body.removeChild(document.getElementById("MPsetting"));
      }

    }, false);
  };
  // return;

})();