// ==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 gesure
link:{ //optional, the target is link/image link...
href: "https://www.baidu.com/",
title: null, textContent: ""
}
target:{
src: "https://www.baidu.com/img/baidu_jgylogo3.gif", //target element arrtibute: src
title: "到百度首页", //target element arrtibute: title
alt: "到百度首页", //target element arrtibute: alt
textContent: "", //target element's text content
nodeName: "IMG", //target element's node name
self:{} //target element itself
}
textSelection:""
}
So, code in textarea shuold be <em>function body.</em>
And, you can add some not frequently used function as "userDefine" function to MP™
</pre>
<a href="https://github.com/woolition/greasyforks/blob/master/mouseGesture/HY-MouseGesture.md" >(● ̄(エ) ̄●)づ <br>Click Me to More(点我看更多介绍)! </a>
`,
options = {
imgSearchEnging: {// image searching
//默认: "null",
"": "",
Baidu: "https://graph.baidu.com/details?isfromtusoupc=1&tn=pc&carousel=0&promotion_name=pc_image_shituindex&extUiData%5bisLogoShow%5d=1&image=U-R-L",
Google: "https://www.google.com/searchbyimage?sbisrc=cr_1_5_2&image_url=U-R-L",
TinEye: "https://www.tineye.com/search?url=U-R-L",
Yandex: "https://yandex.com/images/search?rpt=imageview&url=U-R-L"
},
searchEnging: {// text searching
//默认: "null",
"": "",
Baidu: "https://www.baidu.com/s?wd=",
Google: "https://www.google.com/search?q=",
Bing: "https://www.bing.com/search?q=",
Yahoo: "https://search.yahoo.com/search?p=",
Wiki: "https://en.wikipedia.org/w/index.php?search=",
Taobao: "https://s.taobao.com/search?q=",
Amazon: "https://www.amazon.com/s/&field-keywords=",
Sogou: "https://www.sogou.com/web?query=",
s360: "https://www.haosou.com/s?q="
},
foreGround: {
//默认: "false",
前台打开: "00",
后台打开: "01",
当前打开: "02",
},
nextTab: {
//默认: "true",
右侧标签页: "10",
最后标签页: "11"
}
};
function q(cssSelector){
return document.querySelectorAll(cssSelector);
}
function attr(element,attributeName, attributeValue){
try {
if(attributeValue) element.setAttribute(attributeName, attributeValue);
else return element.getAttribute(attributeName);
} catch(e) {}
}
function each(elementCollect,func){
try{
Array.prototype.forEach.call(elementCollect, (item)=>{func(item);});
}catch(e){}
}
function listen(element, eventType, func){
element.addEventListener(eventType, func, false);
}
function sel(val1, val2){
return val1 == val2 ? 'selected="selected"' : '';
}
function click(evt){
function getName(evt){
if(evt.target.getAttribute('name')){
return evt.target.getAttribute('name');
}else {
if(evt.target.parentElement.getAttribute('name'))
return evt.target.parentElement.getAttribute('name');
else
return evt.target.parentElement.parentElement.getAttribute('name');
}
}
let named = getName(evt);
switch (named) {
case 'mg1':
case 'mg2':
case 'mg3':
each(q('.MPcontent'),(item)=>{
attr(item, 'style', 'display:none;');
});
attr(q('#'+named)[0], 'style', 'display:block;');
each(q('#MPmenu li'),item=>{
attr(item, 'class', ' ');
});
attr(q('[name='+named+']')[0], 'class', 'MPselected');
break;
case 'close':
q('body')[0].removeChild(q('#MPsetting')[0]);
break;
case 'addFunction':
toggleFunsList();
break;
case 'addFunctionLi':
clickToMakeEle();
break;
case 'alias':
attr(evt.target, 'contentEditable', "true");
break;
case 'toggleArgument':
if(evt.target.textContent === "▲"){
evt.target.textContent = "▼";
try{attr(evt.target.parentElement.lastChild,"style","display:none;");}
catch(e){}
}else {
evt.target.textContent = "▲";
try{attr(evt.target.parentElement.lastChild,"style","display:block;");}
catch(e){}
}
break;
case 'clearGesture':
case 'cancelGesture':
modul.captureGesture("", named);
break;
default:
if(cfg.hasOwnProperty(attr(evt.target, 'data-mark')))
addMask();
break;
}
}
function arg2html(argument, type, trk){
let html ="",
argu, i,rand, trackTxt, name,
argValue = [],
agrDetail = [],
description,
selectName;
if(typeof argument === "object")
argu = argument;
else
argu = JSON.parse(argument);
trackTxt = trk || '';
name = argu.name;
html += `<span>${name}</span><span name="alias">${argu.alias ? argu.alias : local[type][name][cfg.language]}</span><b style="visibility:${argu.arg.length ? "visible" : "hidden"};" name="toggleArgument">▲</b><input type="text" name="${name}" value="${trackTxt}" data-mark="${type}" readonly="readonly"><br/><div class="fnArgument">`;
if(argu.arg.length > 0){
argValue = trackTxt ? argu.arg : [];
agrDetail = fnLocal.arg[name].arg;
description = fnLocal.arg[name].description[cfg.language];
for(i in agrDetail){
rand = Math.floor(Math.random()*1000);
switch (agrDetail[i].slice(0,5)) {
case 'texta':
html += `<span><textarea>${mpUnescape(argValue[i])}</textarea><i></i></span>`;
break;
case 'input':
html += '<span><input type="text"><i></i></span>';
break;
case 'check':
html += `<span><input type="checkbox" id="${name + rand}" value=${argValue[i] || false} ${argValue[i] ? "checked" : ''} class="switch" name="fnCheckbox"><label for="${name + rand}" ${argValue[i] ? 'class="switchOn"' : 'class="switchOff"'}>${description[i]}</label></span>`;
break;
case 'selec':
selectName = agrDetail[i].split(':').pop();
html += `<span><input type="text" value=${argValue[i] || ''}><select name="fnSelect">`;
for (let k in options[selectName]){
html += `<option value=${options[selectName][k]} ${sel(argValue[i], options[selectName][k])}>${k}</option>`;
}
html += '</select></span>';
break;
case 'selex':
selectName = agrDetail[i].split(':').pop();
html += `<span><select name="fnSelect">`;
for (let k in options[selectName]){
html += `<option value=${options[selectName][k]} ${sel(argValue[i], options[selectName][k])}>${k}</option>`;
}
html += '</select></span>';
break;
default:
html = `<span style="visibility:hidden;"></span>`;
break;
}
}
}
return html + "</div>";
}
function makeFunsList(){
let color = ['yellow', 'green', 'blue', 'darkcyan'],
html = '',
arg = null;
each(['gesture', 'text', 'link', 'image'], (type)=>{
each(Object.keys(local[type]), (fnName)=>{
if(fnLocal.arg.hasOwnProperty(fnName))
arg = Object.assign({name:fnName},fnLocal.arg[fnName]);
else
arg = {name:fnName,arg:[]};
html += `<li data-type="${type}" data-arg='${JSON.stringify(arg)}' title="${local[type][fnName][cfg.language]}" name="addFunctionLi">
<span class="tag ${color[['gesture', 'text', 'link', 'image'].indexOf(type)]}">
<!--<span>${fnLocal.FunsListTitle[type][cfg.language]}</span>-->
<!--<span>-->${fnName}<!--</span>-->
</span>
</li>`;
});
});
html = `<fieldset id="FunsList" class="FunsListHide">
<h1 name="addFunction">${fnLocal.addFunction[cfg.language]} ➕ </h1><br/>
${html}
</fieldset>`;
return html;
}
function makeDefinedFunsList(type){
let html ='';
each(Object.keys(cfg[type]), item=>{
try {
html += `<li data-arg='${JSON.stringify(cfg[type][item])}' data-type='${type}'>${arg2html(cfg[type][item], type, item)}`;
} catch(e) {}
});
return html;
}
function clickToMakeEle(){
let tarEle = event.target.tagName === 'LI' ? event.target : (event.target.parentNode.tagName === "LI" ? event.target.parentNode : event.target.parentNode.parentNode);
let ele = document.createElement('li');
ele.setAttribute('data-arg', tarEle.dataset.arg);
ele.setAttribute('data-type', tarEle.dataset.type);
ele.innerHTML = arg2html(tarEle.dataset.arg, tarEle.dataset.type);
document.getElementById('mg2').insertBefore(ele, document.querySelector(`#mg2>li`));
listen(ele, 'change', formChange);
listen(ele.childNodes[2].childNodes[0], 'blur', updateConfigUi);
//函数列表收缩, 回滚到顶部
toggleFunsList();
document.documentElement.scrollTo(0, 0);
}
function updateFns(ele){
// check Conflict
if(Object.keys(cfg[ele.dataset.type]).indexOf(ele.childNodes[3].value) > -1){
if(JSON.parse(ele.dataset.arg).name !== cfg[ele.dataset.type][ele.childNodes[3].value].name){
attr(ele, "style", "background:red!important;");
alert("Gesture Conflict (手势冲突) !!!");
return;
}
}
// setting gesture not null
if(JSON.parse(ele.dataset.arg).name === "setting" && !ele.childNodes[3].value){
attr(ele, "style", "background:red!important;");
alert("Setting Gesture Cannot Set Null (设置手势不能为空) !!!");
return;
}
attr(ele, "style", " ");
let typeObject = {};
each(q(`#mg2>li[data-type=${ele.dataset.type}]`), element=>updateItem(element));
function updateItem(item){
let childrens, trk, argValue=[], name, dataArgObject, alias, argumentNodes;
trk = item.childNodes[3].value;
alias = item.childNodes[1].textContent;
//if mouse track is not empty , update Fns
if(trk !== ''){
childrens = item.childNodes[5].childNodes;
dataArgObject = JSON.parse(item.dataset.arg);
each(childrens, item=>{
if(item.firstElementChild.value && item.firstElementChild.value !== "undefined"){
// console.log(item.firstElementChild.nodeName);
// console.log('updateItem..');
if(item.firstElementChild.nodeName === "TEXTAREA")
argValue.push(mpEscape(item.firstElementChild.value));
else
argValue.push(item.firstElementChild.value);
} else{
argValue.push(' ');
}
});
typeObject[trk] = {name: dataArgObject.name, arg: argValue, alias:alias};
}
}
// console.log(typeObject);
cfg[ele.dataset.type] = typeObject;
storage.set('cfg', cfg);
}
function updateConfigUi(e){
let name = attr(e.target, 'name');
switch (name) {
case 'mouseButton':
case 'suppressionKey':
cfg.Gesture[name] = e.target.value;
break;
case 'distanceThreshold':
cfg.Gesture[name] = parseInt(e.target.value);
break;
case 'distanceSensitivity':
cfg.Gesture[name] = parseInt(e.target.value);
break;
case 'Timeout':
cfg.Gesture[name].duration = parseInt(e.target.value);
break;
case 'directions':
case 'language':
cfg[name] = e.target.value;
break;
case 'background':
case 'lineColor':
cfg.Hinter[name] = e.target.value;
attr(e.target, 'style', `background: #${e.target.value} !important;`);
break;
case 'fontSize':
case 'minLineWidth':
case 'maxLineWidth':
case 'lineGrowth':
cfg.Hinter[name] = parseFloat(parseFloat(e.target.value).toFixed(2));
break;
case 'funNotDefine':
cfg.Hinter[name] = e.target.value;
break;
case 'linktextAslink':
case 'dragInTextarea':
cfg.Drag[name] = e.target.checked;
onOff(e, e.target.checked);
break;
default:
if(name === "alias")
updateFns(e.target.parentElement);
else if(name === "fnCheckbox" || name==="fnSelect"){
formChange();
}
return;
}
storage.set('cfg', cfg);
}
function formChange(){
if(event.target.type === 'checkbox'){
event.target.value = event.target.checked;
onOff(event, event.target.checked);
updateFns(event.target.parentElement.parentElement.parentElement);
}
if(event.target.tagName === 'SELECT'){
//event.target.previousElementSibling.value = event.target.value;
//if( event.target.previousElementSibling.tagName === "INPUT") {
if( event.target.previousElementSibling !== null) {//前input元素显示值 判断前元素
event.target.previousElementSibling.value = event.target.value;
}
updateFns(event.target.parentElement.parentElement.parentElement);
}
}
function onOff(e, check) {
if (check) {
attr(e.target.nextElementSibling, 'class', 'switchOn');
} else {
attr(e.target.nextElementSibling, 'class', 'switchOff');
}
}
function addMask(){
let
w=window.innerWidth,
h=window.innerHeight,
px = 0.1*w,
string=`
<svg height="${h}" width="${w}" style="background:#00000080">
<path id="record" d="
M${50}, ${50+px} v-${px} h${px}
M${w-px-50},${50} h${px} v${px}
M${w-50}, ${h-px-50} v${px} h-${px}
M${50+px}, ${h-50} h-${px} v-${px}"
style="stroke:#fff;stroke-width:${w/50};fill:none;"></path>
<text name="clearGesture" x="100" y="${h-100}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">Clear</text>
<text name="A" x="${w-w/2}" y="${h-h/2}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">A</text>
<text name="cancelGesture" x="${w-100-w/6}" y="${h-100}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">Cancle</text>
</svg>`;
let mask = document.createElement('div');
mask.id = "MPMask";
mask.innerHTML = string + '<div id="recorddingGesture"></div>';
document.body.appendChild(mask);
each(q('text[name=clearGesture], text[name=cancelGesture]'), item=>listen(item,"click",click));
attr(q('#MPsetting')[0], "style", "z-index:9999998 !important;");
attr(event.target, "data-flag", "captureGesture");
runtime.captureGesture = true;
}
function toggleFunsList(){
let a = q('#FunsList')[0];
if(attr(a, 'class') === "FunsListHide"){
attr(a, 'class', 'FunsListShow');
}else{
attr(a, 'class', 'FunsListHide');
}
}
return modul;
})();
//========⑥Run===================
//this addStyle is better than GM_addStyle,but not working in CSP tabs
// function addStyle(cssStr,id='MPStyle'){
// try {
// let node = document.createElement('style');
// node.id = id;
// node.textContent = cssStr;
// document.querySelector(':root').appendChild(node);
// } catch(e){}
// }
function addStyle(cssStr,id='MPStyle'){
GM_addStyle(cssStr);
}
addStyle(`
@font-face {
font-family: 'MParrow';
src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAQdAAoAAAAABPAAAQAAAAAAAAAAAAAAAAAAAAAECAAAABVPUy8yAAABYAAAAEQAAABgUc1dNGNtYXAAAAHEAAAARgAAAGAAcgFDZ2x5ZgAAAiAAAADwAAABNKukdSxoZWFkAAAA9AAAADQAAAA2DKcEFmhoZWEAAAEoAAAAHQAAACQEKQIaaG10eAAAAaQAAAAfAAAAJBGtAZVsb2NhAAACDAAAABQAAAAUATIBfm1heHAAAAFIAAAAFQAAACAACwAKbmFtZQAAAxAAAADnAAABe0DXvWtwb3N0AAAD+AAAABAAAAAgAAMAAXjaY2BkYGAA4gfLE97F89t8ZeBkYgCBq07amiD6mu+MRAaB/3cZXzFuAnI5GMDSAEgbC5142mNgZGBgYgACPSApwCDA+IqBkQEVcAIAGeEBSQAAAHjaY2BkYGDgBEIQzQAlkQAAAjsAFgAAAHjaY2Bm/MY4gYGVgYPRhzGNgYHBHUp/ZZBkaGFgYGJg5WSAAUYGJBCQ5poCpAwZLBkf/H/AoMeEpIaRAcpjAAAVNgmoeNpjYmBgYPzCYAbE3lBagImBQQzM/srgA6IBjAwITgB42i2KywmAQBQD57l+e9gCvAoieLd/7ShmnwZCmDBA4WslaLlMkdyzekdv0LFzSuaNQ9Kj+/ebUfNf0iv2YfA7Mb+pBQmvAAAAAAAAABQAJgA6AEwAXgByAIYAmnjaVY8hT8NAGIa/N0tzLJlgbY4LYmI0zekvTTmBuHomcGT9DXMkpD8Bwd+AhIo1wa8CVYfF4DCgm8wV7m6Gqc+8eZ7nI9AlRejwSCdERvAkYqHEQxljarv6zWIau0sEuv79xAtewy4tjJLpPH2q2rZqvtH3GAc6YiWaswlroQfPKLsaVzYe93ZXu90pneML94ElWRuWS/nhILO7qt2uG/K+M7f5OWxQsBJcLAtc9P04YLHeOu2xL1McJayMAtlx74W34YngW7n25tCe5VLoIp/nuAnxzz4eMwrO/zzDScZGG2xK393V74G7q/8AczlNtXjadY7BasJAEIb/mKgVSumh3ucBoiQetHjpod6K4MlLi7CROSzEBDaB0EfoC/hEvoLv0990G0Rwhtn99p9/hwHwiCMCXCLAsD0v0eP94DnEuNMjjDruY8rOHw/ofqcziEZUnvDhuccfn55D+v/1CC8d9/GFb88DPOO83hjnykbetuoqWxaSTpPkmmWlez1k6mQeyyxJF7HYwtbW5OI0V1OpHzHBGhsYOGaJBrJ7/TlhiS2USgVLtYAg5WoJ854uWLGzZx2QtR7BHDHPGbspFi1b/rGoWQY5347OnGU4UW82mfwCMzM4HQB42mNgZkAGjAxoAAAAjgAFSExQRAEARkMJAAAAUGF3J1oAAAAA) format('woff');
}
#MPcanvas{position:fixed;top:0;left:0;z-index:10000000;}
#MPtips{all:initial!important;position:fixed!important;z-index:9999996!important;top:50%!important;left:50%!important;transform:translate(-50%,-50%)!important;font-family:MParrow,"Arial",sans-serif!important;color:white!important;white-space:nowrap!important;line-height:normal!important;text-shadow:1px 1px 5px rgba(0,0,0,0.8)!important;text-align:center!important;padding:25px 20px 20px 20px!important;border-radius:5px!important;font-weight:bold!important; }
`);
//===========update any time=========
GM_addValueChangeListener('cfg', ()=>{
GestureHandler.applySettings(cfg);
Hinter.applySettings(cfg);
});
//when close a tab, save it's url, in order to reopen it: reopenTab
window.addEventListener('unload', function() {
//GM_setValue('latestTab', window.location.href);
}, false);
//used in func: closeOtherTabs
if(!GM_getValue('closeAll','')) GM_setValue('closeAll', Date());
GM_addValueChangeListener('closeAll',function(name, old_value, new_value, remote){if(remote)window.close();});
//===========update any time end=========
GestureHandler.applySettings(cfg);
Hinter.applySettings(cfg);
GestureHandler.enable();
Hinter.enable();
//========Remind===================
//叠加显示
var wrapEle = document.createElement('div');
wrapEle.id = "wrap";
wrapEle.setAttribute('style', '' +
'position:fixed;' +
'right:0px;' +
'top:0px;' +
'width:300px;' +//最大宽度
//'padding:40px;' +
'background-color:rgba(255,255,255,0)!important;' +
'z-index:2147483647!important;' +//显示最顶层
'');
document.body.appendChild(wrapEle);//元素加入body
function showclipboard(text) {
const wrapDiv = document.getElementById("wrap");
var div = document.createElement('div');
div.setAttribute('style', '' +
'display:none!important;' +//去掉直接显示
'left:0px;' +
'top:0px;' +
'margin-left:auto;' +//table块靠右显示
//'position:absolute!important;' +
'font-size:4px!important;' +
'overflow:auto!important;' +
'background-color:rgba(255,255,255,0.8)!important;' +
'font-family:sans-serif,Arial!important;' +
'font-weight:normal!important;' +
'text-align:left!important;' +//左对齐
'color:#000!important;' +
'padding:0.5em 1em!important;' +
'border-radius:3px!important;' +
'border:1px solid #ccc!important;' +
//'max-width:350px!important;' +
'max-height:1216px!important;' +
'z-index:2147483647!important;' +
'');
div.innerHTML = text;
div.style.display = 'table';// 换行显示结果
let fc = wrapDiv.firstElementChild
if (fc) {
wrapDiv.insertBefore(div,fc)
} else {
wrapDiv.appendChild(div);
}
setTimeout(() => {
div.parentNode.removeChild(div);
},6000)
}
})();