鼠标拖拽,按住鼠标左键拖拽选中文本、链接、图片后复制、打开、搜索,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, """).replace(/'/g, "'"); } function mpUnescape(str){ if(!str) return; return str.replace(/"/g,'"').replace(/'/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:'提示文字的字体大小,单位:""px""', en:'Hint text font size,unit:""px""'}, 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:'轨迹的最小宽度,单位:"px""', en:'Track minimum width,unit:"px""'}, maxLineWidth: {zh:'最大宽度', en:'Track maximum width'}, maxLineWidthTitle: {zh:'轨迹的最大宽度,单位:"px""', en:'Track maximum width,unit:"px"'}, lineGrowth: {zh:'增长速度', en:'Track growth speed'}, lineGrowthTitle: {zh:'轨迹的增长速度,单位:"px"', en:'Track growth speed,unit:"px"'}, 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, '')}> </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 g###re 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:9999###!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) } })();