Greasy Fork is available in English.

Greasemonkey Mouse Gestures

Just a Mouse Gestures script

// ==UserScript==
// @name               Greasemonkey Mouse Gestures
// @name:zh-CN         油猴鼠标手势
// @name:zh-TW         油猴滑鼠手勢
// @namespace          hoothin
// @version            0.70
// @description        Just a Mouse Gestures script
// @description:zh-CN  就是个鼠标手势脚本
// @description:zh-TW  就是個滑鼠手勢脚本
// @author             hoothin
// @include            *
// @grant              GM_openInTab
// @grant              GM_setValue
// @grant              GM_getValue
// @grant              GM_registerMenuCommand
// @grant              unsafeWindow
// @grant              GM_info
// @license            MIT License
// @compatible        chrome
// @compatible        firefox
// ==/UserScript==

var lastX,lastY,lastSign,afterGestures,gesturesWords,gesturesContent,gestures,signs;
const defaultFun={
    close:()=>{unsafeWindow.opener=null;unsafeWindow.open('', '_self', '');unsafeWindow.close();},
    openNew:()=>{GM_openInTab('about:newtab', false)},
    scrollToTop:()=>{unsafeWindow.scrollTo(0, 0)},
    scrollToBottom:()=>{unsafeWindow.scrollTo(0, 1073741824)},
    back:()=>{unsafeWindow.history.back()},
    forward:()=>{unsafeWindow.history.forward()},
    reload:()=>{unsafeWindow.location.reload()}
};
function initEventListener(start,move,end,tracer,clientX,clientY,startBool){
    var isMouse=start=="mousedown";
    var moveFun=function(e){
        tracer(clientX(e),clientY(e),isMouse);
        gesturesWords.innerHTML=signs;
        var gesturesWidth=signs.length*51+40;
        gesturesContent.style.width=gesturesWidth+"px";
        gesturesContent.style.marginLeft=-gesturesWidth/2+"px";
    };
    document.addEventListener(start, function(e) {
        if(!startBool || startBool(e)){
            lastX=clientX(e);
            lastY=clientY(e);
            lastSign=signs="";
            document.addEventListener(move, moveFun, false);
        }
    }, false);
    var endFun=function(e) {
        document.removeEventListener(move, moveFun, false);
        setTimeout(function(){if(gesturesContent.parentNode)gesturesContent.parentNode.removeChild(gesturesContent);},500);
        if(signs){
            if(afterGestures)afterGestures();
            for(var g of gestures){
                var gSign=g.gesture;
                if(signs==gSign){
                    if(!isMouse)document.body.appendChild(gesturesContent);
                    var fun=defaultFun[g.fun];
                    if(fun===undefined || !fun){
                        eval(g.fun);
                    }else {
                        fun();
                    }
                    e.stopPropagation();
                    e.preventDefault();
                    e.returnValue=false;
                    e.cancelBubble=true;
                    return false;
                }
            }
            signs="";
        }
    };
    document.addEventListener(end, endFun, false);
    document.addEventListener("mouseup", endFun, false);
}
(function() {
    'use strict';
    var i18n;
    var lang = navigator.appName=="Netscape"?navigator.language:navigator.userLanguage;
    const minLength=256,tg=0.5;
    switch (lang){
        case "zh-CN":
            i18n={
                close:"关闭页面",
                openNew:"新建标签页",
                scrollToTop:"滚动至最上",
                scrollToBottom:"滚动至最下",
                back:"后退",
                forward:"前进",
                reload:"刷新",
                addListener:"点击监听鼠标手势",
                listening:"正在监听鼠标手势",
                bind:"绑定功能",
                custom:"自定义代码",
                ok:"确定",
                del:"删除",
                saved:"已设定的手势",
                alert1:"请先监听手势",
                alert2:"还没有绑定功能",
                alert3:"请输入自定义代码",
                configure:"鼠标手势设置",
                update:"鼠标手势脚本已更新,是否覆盖脚本设置?"
            };
            break;
        default:
            i18n={
                close:"Close tab",
                openNew:"Open new tab",
                scrollToTop:"Scroll to top",
                scrollToBottom:"Scroll to bottom",
                back:"Back",
                forward:"Forward",
                reload:"Reload",
                addListener:"Click to add gesture listener",
                listening:"Listening",
                bind:"Bind function",
                custom:"Custom code",
                ok:"Ok",
                del:"Delete",
                saved:"Saved gestures",
                alert1:"Please add gesture first",
                alert2:"Nothing bind",
                alert3:"Input custom code please",
                configure:"Mouse Gestures - Configure",
                update:"Greasemonkey Mouse Gestures has updated, recover config?"
            };
            break;
    }
    gestures=GM_getValue("gestures");
    if(GM_info.script.version != GM_getValue("gmMouseGestureVersion")){
        if(!gestures || window.confirm(i18n.update))
            gestures=[{gesture:"↓→",fun:"close"},
                  {gesture:"→↑",fun:"openNew"},
                  {gesture:"←↑",fun:"scrollToTop"},
                  {gesture:"←↓",fun:"scrollToBottom"},
                  {gesture:"→↑←",fun:"back"},
                  {gesture:"←↑→",fun:"forward"},
                  {gesture:"↑↓",fun:"reload"},
                  {gesture:"↓↑↓",fun:"var t=((unsafeWindow.getSelection&&unsafeWindow.getSelection())||(document.getSelection&&document.getSelection())||(document.selection&&document.selection.createRange&&document.selection.createRange().text));var e=(document.charset||document.characterSet);if(t!=''){GM_openInTab('http://translate.google.cn/?text='+t+'&hl=zh-CN&langpair=auto|zh-CN&tbb=1&ie='+e,false);}else{GM_openInTab('http://translate.google.cn/translate?u='+encodeURIComponent(location.href)+'&hl=zh-CN&langpair=auto|zh-CN&tbb=1&ie='+e,false);}"},
                  {gesture:"↓↑↓←",fun:'var d=document,b=d.body;with(b.onselectstart=b.oncopy=b.onpaste=b.onkeydown=b.oncontextmenu=b.onmousemove=b.ondragstart=d.oncopy=d.onpaste=null,d.onselectstart=d.oncontextmenu=d.onmousedown=d.onkeydown=function(){return!0},d.wrappedJSObject||d)onmouseup=null,onmousedown=null,oncontextmenu=null;for(var a=d.getElementsByTagName("*"),i=a.length-1;i>=0;i--){var o=a[i];with(o.wrappedJSObject||o)onmouseup=null,onmousedown=null}var h=d.getElementsByTagName("head")[0];if(h){var s=d.createElement("style");s.innerHTML="html,*{user-select:text!important;-moz-user-select:text!important;-webkit-user-select:text!important;-webkit-user-drag:text!important;-khtml-user-select:text!important;-khtml-user-drag:text!important;pointer-events:auto!important;}",h.appendChild(s)}unsafeWindow.Event.prototype.preventDefault=function(){};'},
                  {gesture:"↓↑↓↑",fun:"var d = document, e = d.getElementById('wappalyzer-container') ; if ( e !== null ) { d.body.removeChild(e); } var u = 'https://wappalyzer.com/', t = new Date().getTime(), c = d.createElement('div'), p = d.createElement('div'), l = d.createElement('link'), s = d.createElement('script') ; c.setAttribute('id', 'wappalyzer-container'); l.setAttribute('rel', 'stylesheet'); l.setAttribute('href', u + 'css/bookmarklet.css'); d.head.appendChild(l); p.setAttribute('id', 'wappalyzer-pending'); p.setAttribute('style', 'background-image: url(' + u + 'images/spinner.gif) !important'); c.appendChild(p); s.setAttribute('src', u + 'bookmarklet/wappalyzer.js'); s.onload = function() { s = d.createElement('script'); s.setAttribute('src', u + 'bookmarklet/apps.js'); s.onload = function() { s = d.createElement('script'); s.setAttribute('src', u + 'bookmarklet/driver.js'); c.appendChild(s); }; c.appendChild(s); }; c.appendChild(s); d.body.appendChild(c);"}
                 ];
        GM_setValue("gestures",gestures);
        GM_setValue("gmMouseGestureVersion",GM_info.script.version);
    }
    function tracer(curX,curY,showSign) {
        let distanceX=curX-lastX,distanceY=curY-lastY;
        let distance=distanceX*distanceX+distanceY*distanceY;
        if (distance>minLength) {
            lastX=curX;
            lastY=curY;
            let direction="";
            let slope=Math.abs(distanceY/distanceX);
            if(slope>tg){
                if(distanceY>0) {
                    direction="↓";
                }else{
                    direction="↑";
                }
            }else if(slope<=1/tg) {
                if(distanceX>0) {
                    direction="→";
                }else{
                    direction="←";
                }
            }
            if(lastSign!=direction) {
                signs+=direction;
                lastSign=direction;
                if(showSign)document.body.appendChild(gesturesContent);
            }
        }
    }
    gesturesContent=document.createElement("div");
    gesturesContent.id="gesturesContent";
    gesturesContent.style.cssText="width:300px;height:70px;position:fixed;left:50%;top:50%;margin-top:-25px;margin-left:-150px;z-index:999999999;background-color:#000;border:1px solid;border-radius:10px;opacity:0.65;filter:alpha(opacity=65);box-shadow:5px 5px 20px 0px #000;";
    gesturesContent.innerHTML='<div id="gesturesWords" style="position:absolute;left:20px;top:5px;font:bold 50px \'黑体\';color:#ffffff"></div>';
    gesturesWords=gesturesContent.querySelector("#gesturesWords");
    initEventListener("touchstart","touchmove","touchend",tracer,e=>{return e.changedTouches[0].clientX},e=>{return e.changedTouches[0].clientY});
    initEventListener("mousedown","mousemove","contextmenu",tracer,e=>{return e.clientX},e=>{return e.clientY},e=>{return e.which === 3});
    if(location.href=="https://github.com/hoothin/UserScripts/tree/master/Mouse%20Gestures"){
        var entryContent=document.querySelector("article.entry-content"),mobile=false;
        if(!entryContent){
            mobile=true;
            entryContent=document.querySelector(".files-list");
        }
        var configBody=document.createElement("div");
        configBody.id="configBody";
        configBody.style.cssText="opacity:0.65;filter:alpha(opacity=65);box-shadow:5px 5px 20px 0px #000;height:160px";
        configBody.innerHTML='<div id="configContent" style="position:relative;left:20px;top:5px;"></div>';
        var configContent=configBody.querySelector("#configContent");
        var newOrEdit=document.createElement("div");
        configContent.appendChild(newOrEdit);
        var newOrEditSign=document.createElement("div");
        newOrEditSign.innerHTML="";
        newOrEditSign.style.float="left";
        newOrEdit.appendChild(newOrEditSign);
        var newOrEditBtn=document.createElement("input");
        newOrEditBtn.type="button";
        newOrEditBtn.value=i18n.addListener;
        newOrEdit.appendChild(newOrEditBtn);
        var functionArea=document.createElement("p");
        newOrEdit.appendChild(functionArea);
        var functionSelect=document.createElement("select");
        functionSelect.innerHTML="<option>"+i18n.bind+"</option><option value='custom'>"+i18n.custom+"</option>";
        for(var key in defaultFun){
            var optionStr="<option value='"+key+"'>"+i18n[key]+"</option>";
            functionSelect.innerHTML+=optionStr;
        }
        functionArea.appendChild(functionSelect);
        var newOrEditEval=document.createElement("input");
        newOrEditEval.style.width=(mobile?"230":"750")+"px";
        newOrEditEval.style.display="none";
        functionArea.appendChild(newOrEditEval);
        var newOrEditOkBtn=document.createElement("input");
        newOrEditOkBtn.type="button";
        newOrEditOkBtn.value=i18n.ok;
        newOrEdit.appendChild(newOrEditOkBtn);
        var gesturesHas=document.createElement("p");
        newOrEdit.appendChild(gesturesHas);
        var gesturesSelect=document.createElement("select");
        var refreshGestures=function(){
            gesturesSelect.innerHTML="<option>"+i18n.saved+"</option>";
            gestures.forEach(function(item){
                var optionStr="<option value='"+item.gesture+"'>"+item.gesture+"</option>";
                gesturesSelect.innerHTML+=optionStr;
            });
        };
        refreshGestures();
        gesturesHas.appendChild(gesturesSelect);
        var newOrEditDelBtn=document.createElement("input");
        newOrEditDelBtn.type="button";
        newOrEditDelBtn.value=i18n.del;
        gesturesHas.appendChild(newOrEditDelBtn);
        entryContent.insertBefore(configBody,entryContent.firstChild);
        functionSelect.onchange=function(){
            if(functionSelect.options.selectedIndex===0)return;
            var value=functionSelect.options[functionSelect.options.selectedIndex].value;
            newOrEditEval.style.display=(value=="custom"?"initial":"none");
        };
        gesturesSelect.onchange=function(){
            if(gesturesSelect.options.selectedIndex===0)return;
            var value=gesturesSelect.options[gesturesSelect.options.selectedIndex].value;
            newOrEditSign.innerHTML=value;
            gestures.every(function(item){
                if(item.gesture==value){
                    functionSelect.options[1].selected = true;
                    for (var i=2;i<functionSelect.options.length;i++) {
                        if (functionSelect.options[i].value == item.fun) {
                            functionSelect.options[i].selected = true;
                            break;
                        }
                    }
                    functionSelect.onchange();
                    newOrEditEval.value=item.fun;
                    return false;
                }
                return true;
            });
        };
        newOrEditBtn.onclick=function(e){
            newOrEditBtn.value=i18n.listening;
            newOrEditBtn.setAttribute("disabled","disabled");
            afterGestures=function(){
                newOrEditBtn.removeAttribute("disabled");
                afterGestures=null;
                newOrEditSign.innerHTML=signs;
                newOrEditBtn.value=i18n.addListener;
            };
        };
        newOrEditOkBtn.onclick=function(e){
            if(!newOrEditSign.innerHTML){
                alert(i18n.alert1);
            }else if(functionSelect.options.selectedIndex===0){
                alert(i18n.alert2);
            }else {
                var op=functionSelect.options;
                var selIndex=op.selectedIndex;
                if(selIndex===1 && !newOrEditEval.value){
                    alert(i18n.alert3);
                }else{
                    var code=op[selIndex].value;
                    if(selIndex===1){
                        code=newOrEditEval.value;
                    }
                    for(var index in gestures){
                        if(gestures[index].gesture==newOrEditSign.innerHTML){
                            gestures.splice(index,1);
                            break;
                        }
                    }
                    gestures.push({gesture:newOrEditSign.innerHTML,fun:code});
                    GM_setValue("gestures",gestures);
                    refreshGestures();
                }
            }
        };
        newOrEditDelBtn.onclick=function(e){
            var value=gesturesSelect.options[gesturesSelect.options.selectedIndex].value;
            for(var index in gestures){
                if(gestures[index].gesture==value){
                    gestures.splice(index,1);
                    GM_setValue("gestures",gestures);
                    break;
                }
            }
            refreshGestures();
        };
    }
    function goSetting(){
        GM_openInTab("https://github.com/hoothin/UserScripts/tree/master/Mouse%20Gestures", false);
    }
    GM_registerMenuCommand(i18n.configure, goSetting);
})();