鼠标手势--就是这么拽!

鼠标手势脚本,就是这么拽:支持右键轨迹手势和左键拖拽功能.可以拖拽[文本],[链接]和[图片],支持自定义设置:鼠标画➡⬅(右左)路径,进入设置

  1. // ==UserScript==
  2. // @name MousePlus
  3. // @name:zh-CN 鼠标手势--就是这么拽!
  4. // @namespace https://greasyfork.org/users/104201
  5. // @description HY's mouse gesture script,supports ringt-key draw track functions and left-key drag functions.Drag target can be [Text] & [Links] & [Image] Customizenable → Right click to draw ⇄(right,left) to setting
  6. // @description:zh-CN 鼠标手势脚本,就是这么拽:支持右键轨迹手势和左键拖拽功能.可以拖拽[文本],[链接]和[图片],支持自定义设置:鼠标画➡⬅(右左)路径,进入设置
  7. // @version 2.3
  8. // @include *
  9. // @noframes
  10. // @run-at document-end
  11. // @grant GM_addStyle
  12. // @grant GM_openInTab
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM.setValue
  16. // @grant GM.getValue
  17. // @grant GM_setClipboard
  18. // @grant GM_download
  19. // @grant GM_addValueChangeListener
  20. // @grant GM_notification
  21. // @grant window.close
  22. // @grant GM_getResourceText
  23. // @grant GM_xmlhttpRequest
  24. // ==/UserScript==
  25. /* jshint esversion: 6 */
  26.  
  27. //===============================================================
  28. //Thanks: Robbendebiene's project Gesturefy
  29. // https://github.com/Robbendebiene/Gesturefy [GPL-3.0]
  30. //For: canvas line style & GestureHandler
  31.  
  32. //===============================================================
  33. //Thanks: Jim Lin's userscript 有道划词翻译
  34. // https://greasyfork.org/zh-CN/scripts/15844 [License MIT]
  35. //For: 划词翻译
  36.  
  37.  
  38. (function() {
  39. 'use strict';
  40. //==========①=========================
  41. let storage = {
  42. get: function(name, defaultValue) {
  43. return GM_getValue(name, defaultValue);
  44. },
  45. set: function(name, data) {
  46. return GM_setValue(name, data);
  47. }
  48. },
  49. runtime = {
  50. sendMessage: function(data){
  51. return Promise.resolve(this.processMessage(data));
  52. },
  53. processMessage: function(data){
  54. switch (data.subject) {
  55. case "gestureFrameMousedown":
  56. case "gestureFrameMousemove":
  57. case "gestureFrameMouseup":
  58. gestureHandler.handleMessage(data);
  59. break;
  60. case 'gestureChange':
  61. if(this.captureGesture){
  62. Ui.captureGesture(data.data.gesture, "recorddingGesture");
  63.  
  64. return;
  65. }
  66. try {
  67. let actionName = '';
  68. if(cfg.gesture[data.data.gesture].alias)
  69. actionName = cfg.gesture[data.data.gesture].alias;
  70. else
  71. actionName = local.gesture[cfg.gesture[data.data.gesture].name][cfg.language];
  72. return {action:actionName};
  73. } catch(e) {}
  74. break;
  75. case 'gestureEnd':
  76. if(this.captureGesture){
  77. Ui.captureGesture(data.data.gesture);
  78. return;
  79. }
  80. try {
  81. let action = cfg.gesture[data.data.gesture];
  82. Fn[action.name](action.arg, data.data);
  83. } catch(e) {
  84. // console.log(e);
  85. }
  86. break;
  87. case 'dragChange':
  88. try {
  89. let actionName = '',
  90. typeAndData = getDragFn(data.data);
  91. if(typeAndData[1].alias)
  92. actionName = typeAndData[1].alias;
  93. else
  94. actionName = local[typeAndData[0]][typeAndData[1].name][cfg.language];
  95. return {action:actionName};
  96. } catch(e) {}
  97. break;
  98. case 'dragEnd':
  99. try {
  100. let action = getDragFn(data.data)[1];
  101. Fn[action.name](action.arg, data.data);
  102. } catch(e) {
  103. // console.log(e);
  104. }
  105. break;
  106. default:
  107. break;
  108. }
  109. },
  110. captureGesture:false
  111. },
  112. _cfg = {
  113. Gesture: {
  114. mouseButton: 2,
  115. suppressionKey: "",
  116. distanceThreshold: 10,
  117. distanceSensitivity: 10,
  118. Timeout: {
  119. active: false,
  120. duration: 1
  121. }
  122. },
  123. Hinter: {
  124. background : 'ff0',
  125. fontSize: 40,
  126. lineColor: '0074d990',
  127. minLineWidth: 1,
  128. maxLineWidth: 10,
  129. lineGrowth: 0.6,
  130. funNotDefine: ' (◔ ‸◔)?'
  131. },
  132. Drag: {
  133. linktextAslink: true,
  134. dragInTextarea: true
  135. },
  136. directions: 4,
  137. language: "zh",
  138. gesture:{ // not drag, just gesture
  139. "2": {name:"toTop", arg:[]},
  140. "8": {name:"toBottom", arg:[]},
  141. "4": {name:"back", arg:[]},
  142. "6": {name:"forward", arg:[]},
  143. "86": {name:"close", arg:[]},
  144. "42": {name:"reopenTab",arg:[]},
  145. "64": {name:"setting", arg:[]}
  146. },
  147. text: { // dragText
  148. "8": {name:"copyText", arg:[]},
  149. "6": {
  150. name:"searchText",
  151. arg:["https://www.baidu.com/s?wd=", true, true]
  152. }
  153. },
  154. link: { // drag link
  155. "6": {name:"openLink", arg:[]},
  156. "8": {name:"copyLink", arg:[]}
  157. },
  158. image: { // drag image
  159. "8": {name:"saveImg", arg:[]},
  160. "3": {name:"selectImg", arg:[]},
  161. "6": {
  162. name:"searchImg",
  163. arg:['https://image.baidu.com/n/pc_search?queryImageUrl=U-R-L&uptype=urlsearch', true, true]
  164. },
  165. "2": {name:"copyImgURL", arg:[]},
  166. "4": {name:"selectImg", arg:[]}
  167. },
  168. },
  169. cfg = storage.get('cfg',_cfg),
  170. Fn = {
  171. userDefine: function(argumentArr, data){
  172. try {
  173. new Function("mpArray", "mpData", mpUnescape(argumentArr[0]))(data);
  174. } catch(e) {
  175. console.log(e);
  176. }
  177. },
  178. stopLoading: function() {
  179. window.stop();
  180. },
  181. reload: function() {
  182. history.go(0);
  183. //window.location.reload();
  184. },
  185. reloadNoCache: function() {
  186. window.location.reload(true);
  187. },
  188. close: function() {
  189. window.close();
  190. },
  191. back: function() {
  192. history.back();
  193. },
  194. forward: function() {
  195. history.forward();
  196. },
  197. toTop: function() {
  198. document.documentElement.scrollTo(0, 0);
  199. },
  200. toBottom: function() {
  201. document.documentElement.scrollTo(0, 9999999);
  202. },
  203. reopenTab: function() {
  204. //GreasyMonkdy:
  205. // GM_openInTab(GM_getValue('latestTab'),false);
  206. //TamperMonkey:
  207. GM_openInTab(GM_getValue('latestTab', 'about:blank'), {
  208. active: true
  209. });
  210. },
  211. URLLevelUp: function() {
  212. //当前网址的层次结构向上一层
  213. if (window.location.href[window.location.href.length - 1] === "/")
  214. window.location.href = "../";
  215. else
  216. window.location.href = "./";
  217. },
  218. //clone curren tab ,background
  219. cloneTab: function() {
  220. GM_openInTab(location.href, {
  221. active: false
  222. });
  223. },
  224. //open new blank tab
  225. openBlankTab: function() {
  226. GM_openInTab('about:blank', {
  227. active: true
  228. });
  229. },
  230. //view source
  231. viewSource: function() {
  232. GM_openInTab('view-source:'+location.href, {
  233. active: true
  234. });
  235. },
  236. fkVip: function(argumentArr) {
  237. GM_openInTab(argumentArr[0]+location.href, {active:true});
  238. },
  239. closeOtherTabs: function() {
  240. GM_setValue('closeAll', Date());
  241. },
  242. translateSelect: function() {
  243. window.document.body.addEventListener('mouseup', translate, false);
  244. var context = new AudioContext();
  245. function translate(e) {
  246. var previous = document.querySelector('.youdaoPopup');
  247. if (previous) {
  248. document.body.removeChild(previous);
  249. }
  250. var selectObj = document.getSelection();
  251. if (selectObj.anchorNode.nodeType == 3) {
  252. var word = selectObj.toString();
  253. if (word == '') {
  254. return;
  255. }
  256. word = word.replace('-\n', '');
  257. word = word.replace('\n', ' ');
  258. var ts = new Date().getTime();
  259. var x = e.clientX;
  260. var y = e.clientY;
  261. translate(word, ts);
  262. }
  263. function popup(x, y, result) {
  264. var youdaoWindow = document.createElement('div');
  265. youdaoWindow.classList.toggle('youdaoPopup');
  266. var dict = JSON.parse(result);
  267. var query = dict.query;
  268. var errorCode = dict.errorCode;
  269. if (dict.basic) {
  270. word();
  271. } else {
  272. sentence();
  273. }
  274. youdaoWindow.style.cssText = `z-index:999999;display:block;position:fixed;color:black;text-align:left;word-wrap:break-word;background:lightBlue;border-radius:5px;box-shadow:0 0 5px 0;opacity:1;width:200px;left:${x+10}px;padding:5px`;
  275. if (x + 200 + 10 >= window.innerWidth) {
  276. youdaoWindow.style.left = parseInt(youdaoWindow.style.left) - 200 + 'px';
  277. }
  278. if (y + youdaoWindow.offsetHeight + 10 >= window.innerHeight) {
  279. youdaoWindow.style.bottom = '20px';
  280. } else {
  281. youdaoWindow.style.top = y + 10 + 'px';
  282. }
  283. document.body.appendChild(youdaoWindow);
  284. function word() {
  285. var basic = dict.basic;
  286. var header = document.createElement('p');
  287. var span = document.createElement('span');
  288. span.innerHTML = query;
  289. header.appendChild(span);
  290. var phonetic = basic.phonetic;
  291. if (phonetic) {
  292. var phoneticNode = document.createElement('span');
  293. phoneticNode.innerHTML = '[' + phonetic + ']';
  294. phoneticNode.style.cursor = 'pointer';
  295. header.appendChild(phoneticNode);
  296. phoneticNode.addEventListener('mouseup', function(e) {
  297. e.stopPropagation();
  298. }, false);
  299. var soundUrl = 'https://dict.youdao.com/dictvoice?type=2&audio={}'.replace('{}', query);
  300. var promise = new Promise(function() {
  301. GM_xmlhttpRequest({
  302. method: 'GET',
  303. url: soundUrl,
  304. responseType: 'arraybuffer',
  305. onload: function(res) {
  306. try {
  307. context.decodeAudioData(res.response, function(buffer) {
  308. phoneticNode.addEventListener('mouseup', function() {
  309. var source = context.createBufferSource();
  310. source.buffer = buffer;
  311. source.connect(context.destination);
  312. source.start(0);
  313. }, false);
  314. header.appendChild(document.createTextNode('✓'));
  315. });
  316. } catch (e) {}
  317. }
  318. });
  319. });
  320. promise.then();
  321. }
  322. header.style.color = 'darkBlue';
  323. header.style.margin = '0';
  324. header.style.padding = '0';
  325. span.style.color = 'black';
  326. youdaoWindow.appendChild(header);
  327. var hr = document.createElement('hr');
  328. hr.style.margin = '0';
  329. hr.style.padding = '0';
  330. youdaoWindow.appendChild(hr);
  331. var ul = document.createElement('ul');
  332. ul.style.margin = '0';
  333. ul.style.padding = '0';
  334. basic.explains.map(function(trans) {
  335. var li = document.createElement('li');
  336. li.style.listStyle = 'none';
  337. li.style.margin = '0';
  338. li.style.padding = '0';
  339. li.appendChild(document.createTextNode(trans));
  340. ul.appendChild(li);
  341. });
  342. youdaoWindow.appendChild(ul);
  343. }
  344. function sentence() {
  345. var ul = document.createElement('ul');
  346. ul.style.margin = '0';
  347. ul.style.padding = '0';
  348. dict.translation.map(function(trans) {
  349. var li = document.createElement('li');
  350. li.style.listStyle = 'none';
  351. li.style.margin = '0';
  352. li.style.padding = '0';
  353. li.appendChild(document.createTextNode(trans));
  354. ul.appendChild(li);
  355. });
  356. youdaoWindow.appendChild(ul);
  357. }
  358. }
  359. function translate(word, ts) {
  360. var reqUrl = 'http://fanyi.youdao.com/openapi.do?type=data&doctype=json&version=1.1&relatedUrl=' +
  361. escape('http://fanyi.youdao.com/#') +
  362. '&keyfrom=fanyiweb&key=null&translate=on' +
  363. '&q={}'.replace('{}', word) +
  364. '&ts={}'.replace('{}', ts);
  365. GM_xmlhttpRequest({
  366. method: 'GET',
  367. url: reqUrl,
  368. onload: function(res) {
  369. popup(x, y, res.response);
  370. }
  371. });
  372. }
  373. }
  374. },
  375. contentEditable: function(argumentArr, data){
  376. data.target.self.setAttribute('contenteditable', 'true');
  377. data.target.self.setAttribute('data-mp', '1');
  378. },
  379. /*
  380. //not torking
  381. zoomIn: function(){
  382. setTimeout(zoomer, 200);
  383. function zoomer(evt){
  384. let a, b,isZoom = true;
  385. a = document.elementFromPoint(evt.clientX,evt.clientY).style.zoom=cfg.zoom;
  386. a.setAttribute('data-zoom', 'true');
  387. [].every.forEach(document.querySelectorAll('*[data-zoom=true]'), function(item){
  388. if (item !== a) item.style.zoom = null;
  389. });
  390. }
  391. },*/
  392.  
  393. searchText: function(argumentArr, data) {
  394. GM_openInTab(argumentArr[0] + encodeURIComponent(data.textSelection),
  395. {
  396. active: argumentArr[1] != "false",
  397. insert: argumentArr[2] != "false",
  398. setParent: true //makes the browser re-focus the current tab on close.
  399. });
  400. },
  401. copyText: function(argumentArr, data) {
  402. GM_setClipboard(data.textSelection, "text");
  403. },
  404. openLink: function(argumentArr, data) {
  405. //TamperMonkey
  406. GM_openInTab(getLink(data), {
  407. active: true
  408. });
  409. },
  410. copyLink: function(argumentArr, data) {
  411. GM_setClipboard(getLink(data), "text");
  412. },
  413. copyLinkText: function(argumentArr, data) {
  414. GM_setClipboard(data.target.textContent || data.textSelection, "text");
  415. },
  416. saveImg: function(argumentArr, data) {
  417. //TamperMonkey
  418. let name = data.target.src.split('/').pop();
  419. GM_download(data.target.src, name);
  420. //method 2
  421. /*
  422. let a = document.createElement('a');
  423. a.href = dObj.img; a.setAttribute('download', dObj.img.split('/').pop());
  424. document.documentElement.appendChild(a);
  425. a.click();
  426. a.parentElement.remove(a);
  427. */
  428. /* //jQuery:
  429. $("<a>").attr("href", actionFn.request.selimg).attr("download", actionFn.request.selimg.split('/').pop()).appendTo("body");
  430. a[0].click();
  431. a.remove();
  432. */
  433. },
  434. searchImg: function(argumentArr, data) {
  435. //TamperMonkey
  436. GM_openInTab(argumentArr[0].replace(/U-R-L/, data.target.src), {
  437. active: argumentArr[1] != "false",
  438. insert: argumentArr[2] != "false",
  439. setParent: true //not working
  440. });
  441. },
  442. selectImg: function(argumentArr, data) {
  443. // it may not working on some browsers [develping standard]
  444. //TamperMonkey
  445. document.execCommand('selectAll');
  446. let sel = document.getSelection();
  447. sel.collapse(data.target.self, 0);
  448. sel.modify("extend", "forward", "character");
  449. },
  450. //not working:
  451. copyImage: function(e) {
  452. let canvas = canvasDrawTheImage(e);
  453. // get image as blob
  454. canvas.canvas.toBlob((blob) => {
  455. GM_setClipboard(blob, {
  456. type: canvas.type,
  457. mimetype: canvas.mime
  458. });
  459. }, canvas.mime);
  460. },
  461. image2DataURL: function(e) {
  462. //canvas绘制图片,由于浏览器的安全考虑:
  463. //如果在使用canvas绘图的过程中,使用到了外域的图片资源,那么在toDataURL()时会抛出安全异常:
  464. let canvas = canvasDrawTheImage(e).canvas;
  465. let dataURL = canvas.toDataURL();
  466. GM_setClipboard(dataURL, "text");
  467. },
  468. copyImgURL: function(argumentArr, data) {
  469. //TamperMonkey
  470. GM_setClipboard(data.target.src, "text");
  471. },
  472. openImgNewTab: function(argumentArr, data) {
  473. //TamperMonkey
  474. GM_openInTab(data.target.src, {
  475. active: true
  476. });
  477. },
  478. setting: function() {
  479. if (document.getElementById('MPsetting')) {
  480. return;
  481. }else Ui.init();
  482. }
  483. },
  484. local = {
  485. gesture:{
  486. stopLoading: {zh:'停止加载', en:'StopLoading'},
  487. reload: {zh:'刷新', en:'Refresh'},
  488. reloadNoCache: {zh:'清缓存刷新', en:'Refresh Without Cache'},
  489. close: {zh:'关闭', en:'Close'},
  490. back: {zh:'后退', en:'Back'},
  491. forward: {zh:'前进', en:'Forward'},
  492. toTop: {zh:'到顶部', en:'Scroll to Top'},
  493. toBottom: {zh:'到底部', en:'Scroll to Bottom'},
  494. reopenTab: {zh:'打开最近关闭窗口', en:'Reopen Latest Closed Window'},
  495. setting: {zh:'设置', en:'Settings'},
  496. URLLevelUp: {zh:'网址向上一层', en:'URL hierarchy up'},
  497. cloneTab: {zh:'克隆标签页', en:'Duplicate This Tab'},
  498. openBlankTab: {zh:'打开空白页', en:'Open New Blank Tab'},
  499. viewSource: {zh:'看网页源代码', en:'View Source'},
  500. fkVip: {zh:'破解VIP视频', en:'Crack to Watch VIP Video'},
  501. closeOtherTabs: {zh:'关闭其他标签', en:'Close Other Tabs'},
  502. translateSelect: {zh:'开启划词翻译', en:'Turn on Select And Translate'},
  503. //开发者功能
  504. contentEditable: {zh:'元素内容可编辑', en:'Element Content Editable'},
  505. userDefine: {zh:'自定义', en:'User Define'}
  506. },
  507. //drag text
  508. text: {
  509. searchText: {zh:'搜索选中文本', en:'Search Selected Text'},
  510. copyText: {zh:'复制选中文本', en:'Copy Selected Text'},
  511. userDefine: {zh:'自定义', en:'User Define'}
  512. },
  513. //drag link
  514. link:{
  515. openLink: {zh:'打开链接', en:'Open Link'},
  516. copyLink: {zh:'复制链接', en:'Copy Link'},
  517. copyLinkText: {zh:'复制链接文字', en:'Copy Link Text'},
  518. userDefine: {zh:'自定义', en:'User Define'}
  519. },
  520. //drag image
  521. image:{
  522. saveImg: {zh:'保存图片', en:'Save Image'},
  523. searchImg: {zh:'搜索图片', en:'Search Image'},
  524. // copyImage: {zh:'复制图片', en:'Copy Image to ClickBoard'},
  525. copyImgURL: {zh:'复制图片链接', en:'Copy ImageURL'},
  526. openImgNewTab: {zh:'新标签打开图片', en:'Open Image in New Tab'},
  527. // image2DataURL: {zh:'复制图片为DataURL',en:'Copy Image as DataURL'},
  528. selectImg: {zh:'选中图片', en:'Select This Image'},
  529. userDefine: {zh:'自定义', en:'User Define'}
  530. }
  531. };
  532.  
  533. //========②supported functions=======
  534. function getLink(data){
  535. if(data.link)
  536. return data.link.href;
  537. else if(data.target.src)
  538. return data.target.src;
  539. else return data.textSelection;
  540. }
  541.  
  542. //--> check if string is an url
  543. function isURL (string) {
  544. try {
  545. new URL(string);
  546. }
  547. catch (e) {
  548. return false;
  549. }
  550. return true;
  551. }
  552.  
  553. //--> checks if the current window is framed or not
  554. function inIframe () {
  555. try {
  556. return window.self !== window.top;
  557. }
  558. catch (e) {
  559. return true;
  560. }
  561. }
  562.  
  563. //--> returns all available data of the given target
  564. //--> this data is used by some background actions
  565. function getTargetData(target) {
  566. let data = {};
  567.  
  568. data.target = {
  569. src: target.currentSrc || target.src || null,
  570. title: target.title || null,
  571. alt: target.alt || null,
  572. textContent: target.textContent.trim(),
  573. nodeName: target.nodeName,
  574. self: target
  575. };
  576.  
  577. let link = getClosestLink(target);
  578. if (link) {
  579. data.link = {
  580. href: link.href || null,
  581. title: link.title || null,
  582. textContent: link.textContent.trim()
  583. };
  584. }
  585.  
  586. data.textSelection = getTextSelection();
  587.  
  588. return data;
  589. }
  590.  
  591. //--> returns the selected text, if no text is selected it will return an empty string
  592. //--> inspired by https://stackoverflow.com/a/5379408/3771196
  593. function getTextSelection () {
  594. // get input/textfield text selection
  595. if (document.activeElement &&
  596. typeof document.activeElement.selectionStart === 'number' &&
  597. typeof document.activeElement.selectionEnd === 'number') {
  598. return document.activeElement.value.slice(
  599. document.activeElement.selectionStart,
  600. document.activeElement.selectionEnd
  601. );
  602. }
  603. // get normal text selection
  604. return window.getSelection().toString();
  605. }
  606.  
  607. //--> calculates and returns the distance
  608. //--> between to points
  609. function getDistance(x1, y1, x2, y2) {
  610. return Math.hypot(x2 - x1, y2 - y1);
  611. }
  612.  
  613. //--> returns the closest hierarchical link node or null of given element
  614. function getClosestLink (node) {
  615. // bubble up the hierarchy from the target element
  616. while (node !== null && node.nodeName.toLowerCase() !== "a" && node.nodeName.toLowerCase() !== "area")
  617. node = node.parentElement;
  618. return node;
  619. }
  620. function getDirection(x, y, cx, cy){
  621. /*=================
  622. | |
  623. | 1↖ 2↑ 3↗ |
  624. | |
  625. | 4← 5 6→ |
  626. | |
  627. | 7↙ 8↓ 9↘ |
  628. | |
  629. |=================*/
  630. let d, t;
  631. if(cfg.directions == 4){ //4 directions
  632. if (Math.abs(cx - x) < Math.abs(cy - y)) {
  633. d = cy > y ? "8" : "2";
  634. } else {
  635. d = cx > x ? "6" : "4";
  636. }
  637. }else{ //8 directions
  638. t = (cy-y)/(cx-x);
  639. if (-0.4142<= t && t < 0.4142) d = cx > x ? '6' : "4";
  640. else if(2.4142 <= t || t< -2.4142) d = cy > y ? '8' : '2';
  641. else if(0.4142 <= t && t < 2.4142) d = cx > x ? '9' : '1';
  642. else d = cy > y ? '7' : '3';
  643. }
  644. return d;
  645. }
  646. // data: data.data
  647. function getDragFn(data){
  648. // let
  649. if(data.target.nodeName === "IMG")
  650. return ['image',cfg.image[data.gesture]];
  651. else if(data.link || data.target.nodeName === "A" || isURL(data.textSelection))
  652. return ['link', cfg.link[data.gesture]];
  653. else
  654. return ['text', cfg.text[data.gesture]];
  655. }
  656.  
  657. function mpEscape(str){
  658. if(!str) return;
  659. return str.replace(/"/g, "&quot;").replace(/'/g, "&apos;");
  660. }
  661. function mpUnescape(str){
  662. if(!str) return;
  663. return str.replace(/&quot;/g,'"').replace(/&apos;/g, "'");
  664. }
  665.  
  666. //========③Hinter====================
  667. const Hinter = (function(){
  668. let modul = {};
  669.  
  670. modul.enable = function enable(){
  671. GestureHandler
  672. .on("start", addCanvas)
  673. .on("update", updateTrack)
  674. .on("change", updateHint)
  675. .on("abort", reset)
  676. .on("end", reset);
  677. };
  678.  
  679. modul.applySettings = function applySettings(Config){
  680. background = Config.Hinter.background;
  681. fontSize = Config.Hinter.fontSize;
  682. lineColor = Config.Hinter.lineColor;
  683. minLineWidth = Config.Hinter.minLineWidth;
  684. maxLineWidth = Config.Hinter.maxLineWidth;
  685. lineGrowth = Config.Hinter.lineGrowth;
  686. funNotDefine = Config.Hinter.funNotDefine;
  687. updateHintLayer();
  688. };
  689.  
  690. //private methods & value
  691. let
  692. background = 'ff0',
  693. fontSize = 40,
  694. lineColor = '0074d990',
  695. minLineWidth = 1,
  696. maxLineWidth = 10,
  697. lineGrowth = 0.6,
  698. funNotDefine = ' (◔ ‸◔)?';
  699. let canvas = null,
  700. tip = null,
  701. ctx = null,
  702. hasCanvas = false;
  703.  
  704. function updateHintLayer(){
  705. canvas = tip = ctx = hasCanvas = null;
  706. createCanvaTips();
  707. }
  708. function createCanvaTips(){
  709. //create <canvas>
  710. canvas = document.createElement("canvas");
  711. canvas.id = 'MPcanvas';
  712. ctx = canvas.getContext("2d");
  713. //create tips<div>
  714. tip = document.createElement('div');
  715. tip.id = 'MPtips';
  716. tip.style.cssText = `background:#${background} !important; font-size: ${fontSize}px !important;`;
  717. }
  718. //<canvas> & tip<div> is ready, when mousemove or drag, append to show track & tips
  719. function addCanvas(e) {
  720. if(!canvas || !tip) createCanvaTips();
  721. document.documentElement.appendChild(tip); //append tip <div>
  722. document.documentElement.appendChild(canvas); //append <canvas>
  723. canvas.width = window.innerWidth; //set canvas attribute to clear content
  724. canvas.height = window.innerHeight;
  725. ctx.lineCap = "round";
  726. ctx.lineJoin = "round";
  727. if(lineColor.length>6) canvas.style.opacity = parseInt(lineColor.slice(6),16)/255;
  728. ctx.lineWidth = minLineWidth;
  729. ctx.strokeStyle = '#' + lineColor.slice(0,6); //like delicious link color//line color
  730. hasCanvas = true;
  731. //allow drop
  732. tip.addEventListener('dragover', ()=>event.preventDefault(), false);
  733. canvas.addEventListener('dragover', ()=>event.preventDefault(), false);
  734. }
  735. //remove <canvas> and tips<div>,set flags to false
  736. function reset() {
  737. if (hasCanvas) {
  738. document.documentElement.removeChild(canvas);
  739. tip.innerHTML = '';
  740. document.documentElement.removeChild(tip);
  741. }
  742. hasCanvas = false;
  743. }
  744. //show Tips
  745. function updateHint(gesture,fnName){
  746. tip.innerHTML = gesture.join("") + '<br/>' + (fnName ? fnName : funNotDefine);
  747. }
  748. function updateTrack(x,y){
  749. if (hasCanvas) {
  750. ctx.lineWidth = Math.min(maxLineWidth, ctx.lineWidth += lineGrowth);
  751. ctx.lineTo(x, y);
  752. ctx.stroke();
  753. ctx.closePath();
  754. ctx.beginPath();
  755. ctx.moveTo(x, y);
  756. }
  757. }
  758. // due to modul pattern: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
  759. return modul;
  760. })();
  761.  
  762. //========④GesturedHadler============
  763. //--> GestureHandler "singleton" class using the modul pattern
  764. //--> the handler behaves different depending on whether it's injected in a frame or not
  765. //--> frame: detects gesture start, move, end and sends an indication message
  766. //--> main page: detects whole gesture including frame indication messages and reports it to the background script
  767. //--> provides 4 events: on start, update, change and end
  768. //--> on default the handler is disabled and must be enabled via enable()
  769. //--> REQUIRES: contentCommons.js
  770. const GestureHandler = (function() {
  771. // public variables and methods
  772. let modul = {};
  773.  
  774. //-->Add callbacks to the given events
  775. modul.on = function on(event, callback) {
  776. // if event does not exist or function already applied skip it
  777. if (event in events && !events[event].includes(callback))
  778. events[event].push(callback);
  779. return this;
  780. };
  781.  
  782. //-->applies necessary settings
  783. modul.applySettings = function applySettings(Settings) {
  784. mouseButton = Number(Settings.Gesture.mouseButton);
  785. suppressionKey = Settings.Gesture.suppressionKey;
  786. distanceSensitivity = Settings.Gesture.distanceSensitivity;
  787. distanceThreshold = Settings.Gesture.distanceThreshold;
  788. timeoutActive = Settings.Gesture.Timeout.active;
  789. timeoutDuration = Settings.Gesture.Timeout.duration;
  790. };
  791.  
  792. //-->Add the event listeners
  793. modul.enable = function enable() {
  794. if (inIframe()) {
  795. window.addEventListener('mousedown', handleFrameMousedown, true);
  796. window.addEventListener('mousemove', handleFrameMousemove, true);
  797. window.addEventListener('mouseup', handleFrameMouseup, true);
  798. window.addEventListener('dragstart', handleDragstart, true);
  799. } else {
  800. // chrome.runtime.onMessage.addListener(handleMessage);
  801. window.addEventListener('mousedown', handleMousedown, true);
  802. }
  803. };
  804.  
  805. //-->Remove the event listeners and resets the handler
  806. modul.disable = function disable() {
  807. if (inIframe()) {
  808. window.removeEventListener('mousedown', handleFrameMousedown, true);
  809. window.removeEventListener('mousemove', handleFrameMousemove, true);
  810. window.removeEventListener('mouseup', handleFrameMouseup, true);
  811. window.removeEventListener('dragstart', handleDragstart, true);
  812. } else {
  813. // chrome.runtime.onMessage.removeListener(handleMessage);
  814. window.removeEventListener('mousedown', handleMousedown, true);
  815. window.removeEventListener('mousemove', handleMousemove, true);
  816. window.removeEventListener('mouseup', handleMouseup, true);
  817. window.removeEventListener('contextmenu', handleContextmenu, true);
  818. window.removeEventListener('mouseout', handleMouseout, true);
  819. window.removeEventListener('dragstart', handleDragstart, true);
  820. // reset gesture array, internal state and target data
  821. directions = [];
  822. state = "passive";
  823. targetData = {};
  824. }
  825. };
  826.  
  827. // private variables and methods
  828.  
  829. // setting properties
  830. let mouseButton = 2,
  831. dragButton = 1,//MP
  832. suppressionKey = "",
  833. distanceThreshold = 10,
  834. distanceSensitivity = 10,
  835. timeoutActive = false,
  836. timeoutDuration = 1;
  837.  
  838. // contains all gesture direction letters
  839. let directions = [];
  840.  
  841. // internal state: passive, pending, active
  842. let state = "passive";
  843.  
  844. // holds reference point to current point
  845. let referencePoint = {
  846. x: 0,
  847. y: 0
  848. };
  849.  
  850. // contains the timeout identifier
  851. let timeout = null;
  852.  
  853. // contains relevant data of the target element
  854. let targetData = {};
  855.  
  856. // holds all event callbacks added by on()
  857. let events = {
  858. 'start': [],
  859. 'update': [],
  860. 'change': [],
  861. 'abort': [],
  862. 'end': []
  863. };
  864.  
  865. //-->initializes the gesture to the "pending" state, where it's unclear if the user is starting a gesture or not
  866. //-->requires the current x and y coordinates
  867. function init(x, y) {
  868. // set the initial point
  869. referencePoint.x = x;
  870. referencePoint.y = y;
  871.  
  872. // change internal state
  873. state = "pending";
  874.  
  875. // add gesture detection listeners
  876. window.addEventListener('mousemove', handleMousemove, true);
  877. window.addEventListener('dragstart', handleDragstart, true);
  878. window.addEventListener('drag', handleDrag, true);//MP
  879. window.addEventListener('dragend', handleDragend, true);//MP
  880. window.addEventListener('contextmenu', handleContextmenu, true);
  881. window.addEventListener('mouseup', handleMouseup, true);
  882. window.addEventListener('mouseout', handleMouseout, true);
  883. }
  884.  
  885. //-->Indicates the gesture start and should only be called once untill gesture end
  886. function start() {
  887. // dispatch all binded functions with the current x and y coordinates as parameter on start
  888. events['start'].forEach((callback) => callback(referencePoint.x, referencePoint.y));
  889.  
  890. // change internal state
  891. state = "active";
  892. }
  893.  
  894. //-->Indicates the gesture change and should be called every time the cursor position changes
  895. //-->requires the current x and y coordinates
  896. function update(x, y, dragMark) {
  897. // dispatch all binded functions with the current x and y coordinates as parameter on update
  898. events['update'].forEach((callback) => callback(x, y));
  899.  
  900. // handle timeout
  901. if (timeoutActive) {
  902. // clear previous timeout if existing
  903. if (timeout) window.clearTimeout(timeout);
  904. timeout = window.setTimeout(() => {
  905. // dispatch all binded functions on abort
  906. events['abort'].forEach((callback) => callback());
  907. state = "expired";
  908. // clear directions
  909. directions = [];
  910. }, timeoutDuration * 1000);
  911. }
  912.  
  913. let direction = getDirection(referencePoint.x, referencePoint.y, x, y);
  914.  
  915. if (directions[directions.length - 1] !== direction) {
  916. // add new direction to gesture list
  917. directions.push(direction);
  918.  
  919. // send message to background on gesture change
  920. let message = runtime.sendMessage({
  921. // subject: "gestureChange",
  922. subject: dragMark ? "dragChange" : "gestureChange",//MP
  923. data: Object.assign(//MP
  924. targetData, {
  925. gesture: directions.join("")
  926. })
  927. });
  928. // on response (also fires on no response) dispatch all binded functions with the directions array and the action as parameter
  929. message.then((response) => {
  930. let action = response ? response.action : null;
  931. events['change'].forEach((callback) => callback(directions, action));
  932. });
  933. }
  934.  
  935. // set new reference point
  936. referencePoint.x = x;
  937. referencePoint.y = y;
  938. }
  939.  
  940. //-->Indicates the gesture end and should be called to terminate the gesture
  941. function end(dragMark) {
  942. // dispatch all binded functions on end
  943. events['end'].forEach((callback) => callback(directions));
  944.  
  945. // send directions and target data to background if directions is not empty
  946. if (directions.length) runtime.sendMessage({
  947. // subject: "gestureEnd",
  948. subject: dragMark ? "dragEnd" : "gestureEnd",
  949. data: Object.assign(
  950. targetData, {
  951. gesture: directions.join("")
  952. }
  953. )
  954. });
  955.  
  956. // reset gesture handler
  957. reset();
  958. }
  959.  
  960. //-->Resets the handler to its initial state
  961. function reset() {
  962. // remove gesture detection listeners
  963. window.removeEventListener('mousemove', handleMousemove, true);
  964. window.removeEventListener('mouseup', handleMouseup, true);
  965. window.removeEventListener('contextmenu', handleContextmenu, true);
  966. window.removeEventListener('mouseout', handleMouseout, true);
  967. window.removeEventListener('dragstart', handleDragstart, true);
  968. window.removeEventListener('drag', handleDrag, true);//MP
  969. window.removeEventListener('dragend', handleDragend, true);//MP
  970.  
  971. // reset gesture array, internal state and target data
  972. directions = [];
  973. state = "passive";
  974. targetData = {};
  975.  
  976. if (timeout) {
  977. window.clearTimeout(timeout);
  978. timeout = null;
  979. }
  980. }
  981.  
  982. //-->Handles iframe/background messages which will update the gesture
  983. function handleMessage(message, sender, sendResponse) {
  984.  
  985. switch (message.subject) {
  986. case "gestureFrameMousedown":
  987. // init gesture
  988. init(
  989. Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
  990. Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
  991. );
  992. // save target data
  993. targetData = message.data;
  994. break;
  995.  
  996. case "gestureFrameMousemove":
  997. // calculate distance between the current point and the reference point
  998. let distance = getDistance(referencePoint.x, referencePoint.y,
  999. Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
  1000. Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
  1001. );
  1002. // induce gesture
  1003. if (state === "pending" && distance > distanceThreshold)
  1004. start();
  1005. // update gesture && mousebutton fix: right click on frames is sometimes captured by both event listeners which leads to problems
  1006. else if (state === "active" && distance > distanceSensitivity && mouseButton !== 2) update(
  1007. Math.round(message.data.screenX / window.devicePixelRatio - window.mozInnerScreenX),
  1008. Math.round(message.data.screenY / window.devicePixelRatio - window.mozInnerScreenY)
  1009. );
  1010. break;
  1011.  
  1012. case "gestureFrameMouseup":
  1013. if (state === "active" || state === "expired") end();
  1014. else if (state === "pending") reset();
  1015. break;
  1016. }
  1017. }
  1018.  
  1019. //-->Handles mousedown which will add the mousemove listener
  1020. function handleMousedown(event) {
  1021. // on mouse button and no supression key
  1022. if (event.isTrusted && (event.buttons === mouseButton || event.buttons === dragButton) && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {//MP
  1023. // init gesture
  1024. init(event.clientX, event.clientY);
  1025.  
  1026. // save target to global variable if exisiting
  1027. if (typeof TARGET !== 'undefined') TARGET = event.target;
  1028.  
  1029. // get and save target data
  1030. targetData = getTargetData(event.target);
  1031.  
  1032. // prevent and middle click scroll
  1033. if (mouseButton === 4) event.preventDefault();
  1034. }
  1035. }
  1036.  
  1037. //-->Handles mousemove which will either start the gesture or update it
  1038. function handleMousemove(event) {
  1039. if (event.isTrusted && event.buttons === mouseButton) {
  1040. // calculate distance between the current point and the reference point
  1041. let distance = getDistance(referencePoint.x, referencePoint.y, event.clientX, event.clientY);
  1042.  
  1043. // induce gesture
  1044. if (state === "pending" && distance > distanceThreshold)
  1045. start();
  1046.  
  1047. // update gesture
  1048. else if (state === "active" && distance > distanceSensitivity)
  1049. update(event.clientX, event.clientY);
  1050.  
  1051. // prevent text selection
  1052. if (mouseButton === 1) window.getSelection().removeAllRanges();
  1053. }
  1054. }
  1055.  
  1056. //-->Handles context menu popup and removes all added listeners
  1057. function handleContextmenu(event) {
  1058. if (event.isTrusted && mouseButton === 2) {
  1059. if (state === "active" || state === "expired") {
  1060. // prevent context menu
  1061. event.preventDefault();
  1062. end();
  1063. }
  1064. // reset if state is pending
  1065. else if (state === "pending")
  1066. reset();
  1067. }
  1068. }
  1069.  
  1070. //-->Handles mouseup and removes all added listeners
  1071. function handleMouseup(event) {
  1072. // only call on left and middle mouse click to terminate gesture
  1073. if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4))) {
  1074. if (state === "active" || state === "expired")
  1075. end();
  1076. // reset if state is pending
  1077. else if (state === "pending")
  1078. reset();
  1079. }
  1080. }
  1081.  
  1082. //-->Handles mouse out and removes all added listeners
  1083. function handleMouseout(event) {
  1084. // only call if cursor left the browser window
  1085. if (event.isTrusted && event.relatedTarget === null) {
  1086. if (state === "active" || state === "expired")
  1087. end();
  1088. // reset if state is pending
  1089. else if (state === "pending")
  1090. reset();
  1091. }
  1092. }
  1093.  
  1094. //-->Handles dragstart and prevents it if needed
  1095. function handleDragstart(event) {
  1096. // prevent drag if mouse button and no supression key is pressed
  1097. if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey])))
  1098. event.preventDefault();
  1099. }
  1100.  
  1101. //-->Handles drag MP
  1102. function handleDrag(event) {
  1103. // prevent drag if mouse button and no supression key is pressed
  1104. if (event.isTrusted && event.buttons === dragButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))){
  1105. let distance = getDistance(referencePoint.x, referencePoint.y, event.clientX, event.clientY);
  1106.  
  1107. // induce gesture
  1108. if (state === "pending" && distance > distanceThreshold)
  1109. start();
  1110.  
  1111. // update gesture
  1112. else if (state === "active" && distance > distanceSensitivity)
  1113. update(event.clientX, event.clientY, 'dragMark');
  1114. }
  1115. }
  1116.  
  1117. //-->Handles dragsend MP
  1118. function handleDragend(event) {
  1119. if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4) || event.button === 0 && dragButton === 1)) {//MP
  1120. // if (event.isTrusted && ((event.button === 0 && gestureHandler.mouseButton === 1) || (event.button === 1 && gestureHandler.mouseButton === 4))) {
  1121. if (state === "active" || state === "expired")
  1122. end("dragMark");
  1123. // reset if state is pending
  1124. else if (state === "pending")
  1125. reset();
  1126. }
  1127. }
  1128.  
  1129. //-->Handles mousedown for frames; send message with target data and position
  1130. function handleFrameMousedown(event) {
  1131. // on mouse button and no supression key
  1132. if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {
  1133. runtime.sendMessage({
  1134. subject: "gestureFrameMousedown",
  1135. data: Object.assign(
  1136. getTargetData(event.target), {
  1137. screenX: event.screenX,
  1138. screenY: event.screenY,
  1139. }
  1140. )
  1141. });
  1142. // save target to global variable if exisiting
  1143. if (typeof TARGET !== 'undefined') TARGET = event.target;
  1144. // prevent middle click scroll
  1145. if (mouseButton === 4) event.preventDefault();
  1146. }
  1147. }
  1148.  
  1149. //-->Handles mousemove for frames; send message with position
  1150. function handleFrameMousemove(event) {
  1151. // on mouse button and no supression key
  1152. if (event.isTrusted && event.buttons === mouseButton && (!suppressionKey || (suppressionKey in event && !event[suppressionKey]))) {
  1153. runtime.sendMessage({
  1154. subject: "gestureFrameMousemove",
  1155. data: {
  1156. screenX: event.screenX,
  1157. screenY: event.screenY
  1158. }
  1159. });
  1160. // prevent text selection
  1161. if (mouseButton === 1) window.getSelection().removeAllRanges();
  1162. }
  1163. }
  1164.  
  1165. //--> Handles mouseup for frames
  1166. function handleFrameMouseup(event) {
  1167. // only call on left, right and middle mouse click to terminate or reset gesture
  1168. if (event.isTrusted && ((event.button === 0 && mouseButton === 1) || (event.button === 1 && mouseButton === 4) || (event.button === 2 && mouseButton === 2)))
  1169. runtime.sendMessage({
  1170. subject: "gestureFrameMouseup",
  1171. data: {}
  1172. });
  1173. }
  1174.  
  1175. // due to modul pattern: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html
  1176. return modul;
  1177. })();
  1178.  
  1179. //========⑤Setting===================
  1180. const Ui = (function(){
  1181. let modul = {};
  1182. modul.init = function (){
  1183. addStyle(CSS, 'MPmanageStyle');
  1184.  
  1185. let node = document.createElement('div');
  1186. node.id = 'MPsetting';
  1187. node.innerHTML = menuHTML;
  1188. document.body.appendChild(node);
  1189.  
  1190. //#mg1
  1191. q('#mg1')[0].innerHTML = gestureAndDragHTML;
  1192. //#mg2
  1193. q('#mg2')[0].innerHTML = makeFunsList();
  1194. each(['gesture', 'text', 'link', 'image'],(item)=>{
  1195. q('#mg2')[0].innerHTML += makeDefinedFunsList(item);
  1196. });
  1197. //#mg3
  1198. q('#mg3')[0].innerHTML = aboutHTML;
  1199.  
  1200. //addEventListener
  1201. listen(q('#MPsetting')[0], 'click', click);
  1202. each(q('#mg1 input[type=text], #mg2 span[name="alias"]'),item=>{
  1203. listen(item, 'blur', updateConfigUi);
  1204. });
  1205. each(q('#MPsetting select, #MPsetting input[type=checkbox]'),item=>{
  1206. listen(item, 'change', updateConfigUi);
  1207. });
  1208. //show functions,hide others
  1209. q('li[name=mg2]')[0].click();
  1210. };
  1211.  
  1212. modul.captureGesture = function(gestureStr, operation){
  1213. try {
  1214. if(operation === "recorddingGesture"){
  1215. q('#recorddingGesture')[0].textContent = gestureStr;
  1216. return;
  1217. }
  1218. if(operation !== "cancelGesture") q('[data-flag=captureGesture]')[0].value = gestureStr;
  1219. document.body.removeChild(q('#MPMask')[0]);
  1220. runtime.captureGesture = false;
  1221. attr(q('#MPsetting')[0], "style", " ");
  1222. let tmp = q('[data-flag=captureGesture]')[0];
  1223. attr(tmp, "data-flag", " ");
  1224. updateFns(tmp.parentElement);
  1225. } catch(e) {
  1226. // console.log(e);
  1227. }
  1228. };
  1229.  
  1230. let
  1231. fnLocal = {
  1232. arg: {
  1233. userDefine:{
  1234. description:{zh:['自定义功能代码'], en:['User Define Function Code']},
  1235. arg:['textarea']
  1236. },
  1237. searchText:{
  1238. description:{zh:['搜索引擎', '后台打开', '右边插入'], en:['SearchingEnging', 'Load In Background', 'Insert After Current Tab']},
  1239. arg:['select:searchEnging', 'checkbox', 'checkbox']
  1240. },
  1241. searchImg:{
  1242. description:{zh:['图片搜索引擎', '后台打开', '右边插入'], en:['Image SearchingEnging', 'Load In Background', 'Insert After Current Tab']},
  1243. arg:['select:imgSearchEnging', 'checkbox', 'checkbox']
  1244. },
  1245. fkVip:{
  1246. description:{zh:['视频解析接口'], en:['Videos Parser API']},
  1247. arg:['select:vipAPI']
  1248. }
  1249. },
  1250. FunsListTitle: {
  1251. gesture: {zh:'手势', en:'Gesture'},
  1252. text: {zh:'拖拽文本', en:'Drag Text'},
  1253. link: {zh:'拖拽链接', en:'Drag Link'},
  1254. image: {zh:'拖拽图片', en:'Drag Image'}
  1255. },
  1256. addFunction: {zh:'增加一个功能', en:'Add Function'}
  1257. },
  1258. CSS = `
  1259. #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;}
  1260. #MPmenu *,
  1261. .MPcontent *{border-radius:3px!important;font-size:16px!important;}
  1262. #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;}
  1263. #MPmenu{z-index:999999!important;height:100%!important;width:100px!important;background:#A2B7D2!important;color:white!important;text-align:center!important;}
  1264. #MPmenu li{list-style-type:none!important;border-top:1px dashed white!important;margin:10px 15px!important;cursor:pointer;}
  1265. .MPselected,#MPmenu li:hover{background:white!important;color:#A2B7D2!important;}
  1266. #MPmenu li span{display:block!important;width:40px!important;height:40px!important;font-size:35px!important;font-weight:bold!important;padding:0 15px!important;}
  1267. #MPmenu b{display:block!important;width:70px!important;text-align:center!important;margin-top:10px!important;}
  1268. .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;}
  1269. .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;}
  1270. .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;}
  1271. .MPcontent > li:hover{box-shadow:inset 1px 1px 1px 3px #A2B7D240!important;}
  1272. #mg1 >li span:nth-child(2),#mg2>li>input{max-height:28px!important;float:right!important;}
  1273. #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;}
  1274. .MPcontent input[type="checkbox"]{width:0!important;}
  1275. #FunsList{width:800px!important;border:0!important;overflow:hidden!important;}
  1276. .FunsListHide{height: 34px!important;border: 0!important;margin: 0!important;padding: 0!important;}
  1277. .FunsListShow{height:auto!important;}
  1278. #FunsList>li{display:inline-block!important;width:300px!important;height:30px!important;margin:5px!important;text-align:left!important;}
  1279. 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;}
  1280. span.tag{color:white!important;margin:0!important;border:0!important;padding:1px 7px 3px 0!important;border-radius:4px!important;}
  1281.  
  1282. #mg2 b{margin-left:30px;padding:0 20px;background:#0000000C!important;}
  1283. #mg2 div.fnArgument{display:none;padding-top:20px!important;height:auto;}
  1284. #mg2 div.fnArgument textarea{width:100%;height:200px;}
  1285. #mg2 div.fnArgument span{width:auto;height:auto;}
  1286. #mg2 .yellow{background:#FFB400!important;}
  1287. #mg2 .yellow:before{content:"${fnLocal.FunsListTitle.gesture[cfg.language]}";}
  1288. #mg2 .blue:before{content:"${fnLocal.FunsListTitle.link[cfg.language]}";}
  1289. #mg2 .blue{background:#1182C2!important;}
  1290. #mg2 .green:before{content:"${fnLocal.FunsListTitle.text[cfg.language]}";}
  1291. #mg2 .green{background:#4DC71F!important;}
  1292. #mg2 .darkcyan:before{content:"${fnLocal.FunsListTitle.image[cfg.language]}";}
  1293. #mg2 .darkcyan{background:#B10DC9!important;}
  1294. #mg2 > li[data-type=gesture]>span:first-child{background:#FFB40030!important;color:#FFB400!important;}
  1295. #mg2 > li[data-type=text]>span:first-child{background:#4DC71F30!important;color:#4DC71F!important;}
  1296. #mg2 > li[data-type=link]>span:first-child{background:#1182C230!important;color:#1182C2!important;}
  1297. #mg2 > li[data-type=image]>span:first-child{background:#B10DC930!important;color:#B10DC9!important;}
  1298. #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;}
  1299. #mg2>li>span{margin-bottom:10px!important;}
  1300. #mg2>li>input {font-family: MParrow;}
  1301.  
  1302. #mg2 div input[type=text],#mg2 div select{background:#0000000c;padding:5px;margin:10px 5px;border: 0;}
  1303. #mg2 div input{width:80%;}
  1304. #mg2 div select{width:15%;}
  1305. #mg2 div label{margin:3px 0;}
  1306.  
  1307.  
  1308. #mg3 *{height: auto;font-size: 30px!important;text-decoration: none;font-weight: bolder;padding: 20px; color:#3A3B74!important}
  1309.  
  1310. /*label 作为开关*/
  1311. 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;}
  1312. 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;}
  1313. input[type=checkbox].switch{width:0px!important;}
  1314.  
  1315. #MPMask{z-index:9999999;position:fixed;top:0;left:0;}
  1316. #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;}
  1317. `,
  1318. uiLocal = {
  1319. //gesture
  1320. gestureUi: {zh:'手势配置', en:'Gesture Config'},
  1321. mouseButton: {zh:'手势按键', en:'Gesture mouse button'},
  1322. leftButton: {zh:'左键', en:'Left Key'},
  1323. middleButton: {zh:'中键', en:'MIddle Key'},
  1324. rightButton: {zh:'右键', en:'Right Key'},
  1325. mouseButtonTitle: {zh:'触发鼠标手势的按键', en:'The mouse button which will trigger the gesture.'},
  1326.  
  1327. suppressionKey: {zh:'手势禁用键', en:'Gesture suppression key'},
  1328. suppressionKeyTitle: {zh:'按下禁用键,暂时禁用手势', en:'Disables the mouse gesture if the key is pressed.'},
  1329. distanceThreshold: {zh:'手势距离阈值', en:'Gesture distance threshold'},
  1330. distanceThresholdTitle: {zh:'激活鼠标手势的最短距离', en:'The minimum mouse distance until the Gesture gets activated.'},
  1331. distanceSensitivity: {zh:'手势灵敏度', en:'Gesture sensitivity'},
  1332. distanceSensitivityTitle: {zh:'认定为新方向的最短距离。这也影响轨迹平滑度', en:'The minimum mouse distance until a new direction gets recognized. This will also impact the trace smoothness.'},
  1333. timeout: {zh:'手势超时', en:'Gesture timeout'},
  1334. timeoutTitle: {zh:'鼠标不动指定时间后,取消手势', en:'Cancels the gesture after the mouse has not been moved for the specified time.'},
  1335. directions: {zh:'手势方向数', en:'Gesture directions'},
  1336. directionsTitle: {zh:'手势识别的方向个数', en:'Gesture diffrent directions.'},
  1337. language: {zh:'语言', en:'Language'},
  1338. languageTitle: {zh:'设定使用语言', en:'Set the language for using.'},
  1339. //hint
  1340. hintUi: {zh:'提示配置', en:'Hint Config'},
  1341. background: {zh:'提示背景颜色', en:'Hint background'},
  1342. backgroundTitle: {zh:'提示的文字的背景颜色', en:'Hint text background color'},
  1343. fontSize: {zh:'提示字体', en:'Hint font size'},
  1344. fontSizeTitle: {zh:'提示文字的字体大小,单位:"&quot;px"&quot;', en:'Hint text font size,unit:"&quot;px"&quot;'},
  1345. lineColor: {zh:'轨迹颜色', en:'Track line color'},
  1346. lineColorTitle: {zh:'显示轨迹的颜色,十六进制,可以使3/6/8位', en:'track line color, hex, 3/6/8 bit'},
  1347. minLineWidth: {zh:'最小宽度', en:'Track minimum width'},
  1348. minLineWidthTitle: {zh:'轨迹的最小宽度,单位:&quot;px"&quot;', en:'Track minimum width,unit:&quot;px"&quot;'},
  1349. maxLineWidth: {zh:'最大宽度', en:'Track maximum width'},
  1350. maxLineWidthTitle: {zh:'轨迹的最大宽度,单位:&quot;px"&quot;', en:'Track maximum width,unit:&quot;px&quot;'},
  1351. lineGrowth: {zh:'增长速度', en:'Track growth speed'},
  1352. lineGrowthTitle: {zh:'轨迹的增长速度,单位:&quot;px&quot;', en:'Track growth speed,unit:&quot;px&quot;'},
  1353. funNotDefine: {zh:'未定义提示', en:'Gesture not found hint'},
  1354. funNotDefineTitle: {zh:'手势或者功能未定义时的提示信息', en:'If gesture not found, hint this'},
  1355. //drag
  1356. dragSetting: {zh:'拖拽配置', en:'Drag Config'},
  1357. linktextAslink: {zh:'链接优先', en:'Link priority'},
  1358. linktextAslinkTitle: {zh:'链接文字识别为链接', en:'Text link drag as link'},
  1359. dragInTextarea: {zh:'文本框拖拽', en:'Enable drag in textarea'},
  1360. dragInTextareaTitle: {zh:'文本框中选中文字并且拖拽时候,使用拖拽的功能', en:'Enable drag in textarea or input'}
  1361. },
  1362.  
  1363. menuHTML = `
  1364. <div id="MPmenu">
  1365. <span id="MPlogo">
  1366. <svg width="80px" height="100px" viewbox="0 0 200 200">
  1367. <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>
  1368. <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>
  1369. </svg>
  1370. </span>
  1371. <li name="mg1"> <span>◧</span> <b>Config</b> </li>
  1372. <li name="mg2"> <span>↯</span> <b>Gesture</b> </li>
  1373. <li name="mg3"> <span>❓</span> <b>About</b> </li>
  1374. <li name="close"> <span>?</span> <b>Close</b> </li>
  1375. </div>
  1376. <div id="mg1" class="MPcontent">mg1</div>
  1377. <div id="mg2" class="MPcontent">mg2</div>
  1378. <div id="mg3" class="MPcontent">mg3</div>
  1379. `,
  1380. gestureAndDragHTML =
  1381. //======gestureAndDragHTML======
  1382. `
  1383. <h1>${uiLocal.gestureUi[cfg.language]}</h1>
  1384. <!-- 因为启用了左键作为拖拽,所以按钮选项要禁用
  1385. <li>
  1386. <span title="${uiLocal.mouseButtonTitle[cfg.language]}">${uiLocal.mouseButton[cfg.language]}</span>
  1387. <span>
  1388. <select name="mouseButton">
  1389. <option value="0" ${sel(cfg.Gesture.mouseButton, 0)}>${uiLocal.leftButton[cfg.language]}</option>
  1390. <option value="1" ${sel(cfg.Gesture.mouseButton, 1)}>${uiLocal.middleButton[cfg.language]}</option>
  1391. <option value="2" ${sel(cfg.Gesture.mouseButton, 2)}>${uiLocal.rightButton[cfg.language]}</option>
  1392. </select>
  1393. </span>
  1394. </li>
  1395. -->
  1396. <li>
  1397. <span title="${uiLocal.suppressionKeyTitle[cfg.language]}">${uiLocal.suppressionKey[cfg.language]}</span>
  1398. <span>
  1399. <select name="suppressionKey">
  1400. <option value="" ${sel(cfg.Gesture.suppressionKey, '')}>&nbsp;</option>
  1401. <option value="altKey" ${sel(cfg.Gesture.suppressionKey, 'altKey')}>Alt</option>
  1402. <option value="ctrlKey" ${sel(cfg.Gesture.suppressionKey, 'ctrlKey')}>Ctrl</option>
  1403. <option value="shiftKey" ${sel(cfg.Gesture.suppressionKey, 'shiftKey')}>Shift</option>
  1404. </select>
  1405. </span>
  1406. </li>
  1407. <li>
  1408. <span title="${uiLocal.distanceThresholdTitle[cfg.language]}">${uiLocal.distanceThreshold[cfg.language]}</span>
  1409. <span>
  1410. <input type="text" name="distanceThreshold" value="${cfg.Gesture.distanceThreshold}" data-mark="number">
  1411. </span>
  1412. </li>
  1413. <li>
  1414. <span title="${uiLocal.distanceSensitivityTitle[cfg.language]}">${uiLocal.distanceSensitivity[cfg.language]}</span>
  1415. <span>
  1416. <input type="text" name="distanceSensitivity" value="${cfg.Gesture.distanceSensitivity}" data-mark="number">
  1417. </span>
  1418. </li>
  1419. <li>
  1420. <span title="${uiLocal.timeoutTitle[cfg.language]}">${uiLocal.timeout[cfg.language]}</span>
  1421. <span>
  1422. <input type="text" name="timeout" value="${cfg.Gesture.Timeout.duration}" data-mark="number">
  1423. </span>
  1424. </li>
  1425. <li>
  1426. <span title="${uiLocal.directionsTitle[cfg.language]}">${uiLocal.directions[cfg.language]}</span>
  1427. <span>
  1428. <select name="directions">
  1429. <option value="4" ${sel(cfg.directions, 4)}> 4 </option>
  1430. <option value="8" ${sel(cfg.directions, 8)}> 8 </option>
  1431. </select>
  1432. </span>
  1433. </li>
  1434. <li>
  1435. <span title="${uiLocal.languageTitle[cfg.language]}">${uiLocal.language[cfg.language]}</span>
  1436. <span>
  1437. <select name="language">
  1438. <option value="zh" ${sel(cfg.language, 'zh')}>中文</option>
  1439. <option value="en" ${sel(cfg.language, 'en')}>English</option>
  1440. </select>
  1441. </span>
  1442. </li>
  1443. <h1>${uiLocal.hintUi[cfg.language]}</h1>
  1444. <li>
  1445. <span title="${uiLocal.backgroundTitle[cfg.language]}">${uiLocal.background[cfg.language]}</span>
  1446. <span>
  1447. <input type="text" name="background" value="${cfg.Hinter.background}" style="background:#${cfg.Hinter.background} !important;">
  1448. </span>
  1449. </li>
  1450. <li>
  1451. <span title="${uiLocal.fontSizeTitle[cfg.language]}">${uiLocal.fontSize[cfg.language]}</span>
  1452. <span>
  1453. <input type="text" name="fontSize" value="${cfg.Hinter.fontSize}" data-mark="number">
  1454. </span>
  1455. </li>
  1456. <li>
  1457. <span title="${uiLocal.lineColorTitle[cfg.language]}">${uiLocal.lineColor[cfg.language]}</span>
  1458. <span>
  1459. <input type="text" name="lineColor" value="${cfg.Hinter.lineColor}" style="background:#${cfg.Hinter.lineColor} !important;">
  1460. </span>
  1461. </li>
  1462. <li>
  1463. <span title="${uiLocal.minLineWidthTitle[cfg.language]}">${uiLocal.minLineWidth[cfg.language]}</span>
  1464. <span>
  1465. <input type="text" name="minLineWidth" value="${cfg.Hinter.minLineWidth}">
  1466. </span>
  1467. </li>
  1468. <li>
  1469. <span title="${uiLocal.maxLineWidthTitle[cfg.language]}">${uiLocal.maxLineWidth[cfg.language]}</span>
  1470. <span>
  1471. <input type="text" name="maxLineWidth" value="${cfg.Hinter.maxLineWidth}">
  1472. </span>
  1473. </li>
  1474. <li>
  1475. <span title="${uiLocal.lineGrowthTitle[cfg.language]}">${uiLocal.lineGrowth[cfg.language]}</span>
  1476. <span>
  1477. <input type="text" name="lineGrowth" value="${cfg.Hinter.lineGrowth}">
  1478. </span>
  1479. </li>
  1480. <li>
  1481. <span title="${uiLocal.funNotDefineTitle[cfg.language]}">${uiLocal.funNotDefine[cfg.language]}</span>
  1482. <span>
  1483. <input type="text" name="funNotDefine" value="${cfg.Hinter.funNotDefine}">
  1484. </span>
  1485. </li>
  1486.  
  1487. <h1>${uiLocal.dragSetting[cfg.language]}</h1>
  1488. <li>
  1489. <span title="${uiLocal.linktextAslinkTitle[cfg.language]}">${uiLocal.linktextAslink[cfg.language]}</span>
  1490. <span>
  1491. <input type="checkbox" id="linktextAslink" name="linktextAslink" checked="" class="switch">
  1492. <label for="linktextAslink" class="switchOn"></label>
  1493. </span>
  1494. </li>
  1495. <!-- 使用抑制键代替
  1496. <li>
  1497. <span title="${uiLocal.dragInTextareaTitle[cfg.language]}">${uiLocal.dragInTextarea[cfg.language]}</span>
  1498. <span>
  1499. <input type="checkbox" id="dragInTextarea" name="dragInTextarea" checked="" class="switch">
  1500. <label for="dragInTextarea" class="switchOn"></label>
  1501. </span>
  1502. </li>
  1503. -->
  1504. `,
  1505.  
  1506. //=======gestureAndDragHTML End=========
  1507. aboutHTML = `
  1508. <pre style="font-size:1.2em !important;">
  1509. About userDefine function:
  1510. there are one argument(Object:mpData) provided in userDefine function.
  1511. mpData is a object like this:
  1512. {
  1513. gesture:"646", //gesture code of last mouse gesure
  1514. link:{ //optional, the target is link/image link...
  1515. href: "https://www.baidu.com/",
  1516. title: null, textContent: ""
  1517. }
  1518. target:{
  1519. src: "https://www.baidu.com/img/baidu_jgylogo3.gif", //target element arrtibute: src
  1520. title: "到百度首页", //target element arrtibute: title
  1521. alt: "到百度首页", //target element arrtibute: alt
  1522. textContent: "", //target element's text content
  1523. nodeName: "IMG", //target element's node name
  1524. self:{} //target element itself
  1525. }
  1526. textSelection:""
  1527. }
  1528. So, code in textarea shuold be <em>function body.</em>
  1529. And, you can add some not frequently used function as "userDefine" function to MP
  1530. </pre>
  1531. <a href="https://github.com/woolition/greasyforks/blob/master/mouseGesture/HY-MouseGesture.md" >(● ̄(エ) ̄●)づ <br>Click Me to More(点我看更多介绍)! </a>
  1532. `,
  1533. options = {
  1534. imgSearchEnging: { // image searching
  1535. BaiduImage: "https://image.baidu.com/n/pc_search?queryImageUrl=U-R-L&uptype=urlsearch",
  1536. GoogleImage: "https://www.google.com/searchbyimage?image_url=U-R-L",
  1537. TinEye: "http://www.tineye.com/search?url=U-R-L"
  1538. },
  1539. searchEnging: { // text searching
  1540. google: "http://www.google.com/search?q=",
  1541. baidu: "http://www.baidu.com/s?wd=",
  1542. yandex: "http://www.yandex.com/yandsearch?text=",
  1543. Bing: "http://www.bing.com/search?q=",
  1544. yahoo: "http://search.yahoo.com/search?p=",
  1545. wiki: "http://en.wikipedia.org/w/index.php?search=",
  1546. taobao: "http://s.taobao.com/search?q=",
  1547. amazon: "http://www.amazon.com/s/&field-keywords=",
  1548. sogou: "https://www.sogou.com/web?query=",
  1549. s360: "http://www.haosou.com/s?q="
  1550. },
  1551. vipAPI:{
  1552. 疯狂: "http://goudidiao.com/?url=",
  1553. 噗噗: "http://pupudy.com/play?make=url&id="
  1554. }
  1555. };
  1556.  
  1557.  
  1558. function q(cssSelector){
  1559. return document.querySelectorAll(cssSelector);
  1560. }
  1561. function attr(element,attributeName, attributeValue){
  1562. try {
  1563. if(attributeValue) element.setAttribute(attributeName, attributeValue);
  1564. else return element.getAttribute(attributeName);
  1565. } catch(e) {}
  1566. }
  1567. function each(elementCollect,func){
  1568. try{
  1569. Array.prototype.forEach.call(elementCollect, (item)=>{func(item);});
  1570. }catch(e){}
  1571. }
  1572. function listen(element, eventType, func){
  1573. element.addEventListener(eventType, func, false);
  1574. }
  1575. function sel(val1, val2){
  1576. return val1 == val2 ? 'selected="selected"' : '';
  1577. }
  1578. function click(evt){
  1579. function getName(evt){
  1580. if(evt.target.getAttribute('name')){
  1581. return evt.target.getAttribute('name');
  1582. }else {
  1583. if(evt.target.parentElement.getAttribute('name'))
  1584. return evt.target.parentElement.getAttribute('name');
  1585. else
  1586. return evt.target.parentElement.parentElement.getAttribute('name');
  1587. }
  1588. }
  1589. let named = getName(evt);
  1590. switch (named) {
  1591. case 'mg1':
  1592. case 'mg2':
  1593. case 'mg3':
  1594. each(q('.MPcontent'),(item)=>{
  1595. attr(item, 'style', 'display:none;');
  1596. });
  1597. attr(q('#'+named)[0], 'style', 'display:block;');
  1598. each(q('#MPmenu li'),item=>{
  1599. attr(item, 'class', ' ');
  1600. });
  1601. attr(q('[name='+named+']')[0], 'class', 'MPselected');
  1602. break;
  1603. case 'close':
  1604. q('body')[0].removeChild(q('#MPsetting')[0]);
  1605. break;
  1606. case 'addFunction':
  1607. toggleFunsList();
  1608. break;
  1609. case 'addFunctionLi':
  1610. clickToMakeEle();
  1611. break;
  1612. case 'alias':
  1613. attr(evt.target, 'contentEditable', "true");
  1614. break;
  1615. case 'toggleArgument':
  1616. if(evt.target.textContent === "▼"){
  1617. evt.target.textContent = "▲";
  1618. try{attr(evt.target.parentElement.lastChild,"style","display:block;");}
  1619. catch(e){}
  1620. }else {
  1621. evt.target.textContent = "▼";
  1622. try{attr(evt.target.parentElement.lastChild,"style"," ");}
  1623. catch(e){}
  1624. }
  1625. break;
  1626. case 'clearGesture':
  1627. case 'cancelGesture':
  1628. modul.captureGesture("", named);
  1629. break;
  1630. default:
  1631. if(cfg.hasOwnProperty(attr(evt.target, 'data-mark')))
  1632. addMask();
  1633. break;
  1634. }
  1635. }
  1636. function arg2html(argument, type, trk){
  1637. let html ="",
  1638. argu, i,rand, trackTxt, name,
  1639. argValue = [],
  1640. agrDetail = [],
  1641. description,
  1642. selectName;
  1643. if(typeof argument === "object")
  1644. argu = argument;
  1645. else
  1646. argu = JSON.parse(argument);
  1647. trackTxt = trk || '';
  1648. name = argu.name;
  1649. 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">`;
  1650. if(argu.arg.length > 0){
  1651. argValue = trackTxt ? argu.arg : [];
  1652. agrDetail = fnLocal.arg[name].arg;
  1653. description = fnLocal.arg[name].description[cfg.language];
  1654. for(i in agrDetail){
  1655. rand = Math.floor(Math.random()*1000);
  1656. switch (agrDetail[i].slice(0,5)) {
  1657. case 'texta':
  1658. html += `<span><textarea>${mpUnescape(argValue[i])}</textarea><i></i></span>`;
  1659. break;
  1660. case 'input':
  1661. html += '<span><input type="text"><i></i></span>';
  1662. break;
  1663. case 'check':
  1664. 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>`;
  1665. break;
  1666. case 'selec':
  1667. selectName = agrDetail[i].split(':').pop();
  1668. html += `<span><input type="text" value=${argValue[i] || ''}><select name="fnSelect">`;
  1669. for (let k in options[selectName]){
  1670. html += `<option value=${options[selectName][k]}>${k}</option>`;
  1671. }
  1672. html += '</select></span>';
  1673. break;
  1674. default:
  1675. html = `<span style="visibility:hidden;"></span>`;
  1676. break;
  1677. }
  1678. }
  1679. }
  1680. return html + "</div>";
  1681. }
  1682. function makeFunsList(){
  1683. let color = ['yellow', 'green', 'blue', 'darkcyan'],
  1684. html = '',
  1685. arg = null;
  1686. each(['gesture', 'text', 'link', 'image'], (type)=>{
  1687. each(Object.keys(local[type]), (fnName)=>{
  1688. if(fnLocal.arg.hasOwnProperty(fnName))
  1689. arg = Object.assign({name:fnName},fnLocal.arg[fnName]);
  1690. else
  1691. arg = {name:fnName,arg:[]};
  1692. html += `<li data-type="${type}" data-arg='${JSON.stringify(arg)}' title="${local[type][fnName][cfg.language]}" name="addFunctionLi">
  1693. <span class="tag ${color[['gesture', 'text', 'link', 'image'].indexOf(type)]}">
  1694. <!--<span>${fnLocal.FunsListTitle[type][cfg.language]}</span>-->
  1695. <!--<span>-->${fnName}<!--</span>-->
  1696. </span>
  1697. </li>`;
  1698. });
  1699. });
  1700. html = `<fieldset id="FunsList" class="FunsListHide">
  1701. <h1 name="addFunction">${fnLocal.addFunction[cfg.language]} </h1><br/>
  1702. ${html}
  1703. </fieldset>`;
  1704. return html;
  1705. }
  1706. function makeDefinedFunsList(type){
  1707. let html ='';
  1708. each(Object.keys(cfg[type]), item=>{
  1709. try {
  1710. html += `<li data-arg='${JSON.stringify(cfg[type][item])}' data-type='${type}'>${arg2html(cfg[type][item], type, item)}`;
  1711. } catch(e) {}
  1712. });
  1713. return html;
  1714. }
  1715. function clickToMakeEle(){
  1716. let tarEle = event.target.tagName === 'LI' ? event.target : (event.target.parentNode.tagName === "LI" ? event.target.parentNode : event.target.parentNode.parentNode);
  1717. let ele = document.createElement('li');
  1718. ele.setAttribute('data-arg', tarEle.dataset.arg);
  1719. ele.setAttribute('data-type', tarEle.dataset.type);
  1720. ele.innerHTML = arg2html(tarEle.dataset.arg, tarEle.dataset.type);
  1721. document.getElementById('mg2').insertBefore(ele, document.querySelector(`#mg2>li`));
  1722. listen(ele.childNodes[2].childNodes[0], 'blur', updateConfigUi);
  1723. //函数列表收缩, 回滚到顶部
  1724. toggleFunsList();
  1725. document.documentElement.scrollTo(0, 0);
  1726. }
  1727. function updateFns(ele){
  1728. // check Conflict
  1729. if(Object.keys(cfg[ele.dataset.type]).indexOf(ele.childNodes[3].value) > -1){
  1730. if(JSON.parse(ele.dataset.arg).name !== cfg[ele.dataset.type][ele.childNodes[3].value].name){
  1731. attr(ele, "style", "background:red!important;");
  1732. alert("Gesture Conflict (手势冲突) !!!");
  1733. return;
  1734. }
  1735. }
  1736. // setting gesture not null
  1737. if(JSON.parse(ele.dataset.arg).name === "setting" && !ele.childNodes[3].value){
  1738. attr(ele, "style", "background:red!important;");
  1739. alert("Setting Gesture Cannot Set Null (设置手势不能为空) !!!");
  1740. return;
  1741. }
  1742. attr(ele, "style", " ");
  1743.  
  1744. let typeObject = {};
  1745. each(q(`#mg2>li[data-type=${ele.dataset.type}]`), element=>updateItem(element));
  1746. function updateItem(item){
  1747. let childrens, trk, argValue=[], name, dataArgObject, alias, argumentNodes;
  1748. trk = item.childNodes[3].value;
  1749. alias = item.childNodes[1].textContent;
  1750. //if mouse track is not empty , update Fns
  1751. if(trk !== ''){
  1752. childrens = item.childNodes[5].childNodes;
  1753. dataArgObject = JSON.parse(item.dataset.arg);
  1754. each(childrens, item=>{
  1755. if(item.firstElementChild.value && item.firstElementChild.value !== "undefined"){
  1756. // console.log(item.firstElementChild.nodeName);
  1757. // console.log('updateItem..');
  1758. if(item.firstElementChild.nodeName === "TEXTAREA")
  1759. argValue.push(mpEscape(item.firstElementChild.value));
  1760. else
  1761. argValue.push(item.firstElementChild.value);
  1762. } else{
  1763. argValue.push(' ');
  1764. }
  1765. });
  1766. typeObject[trk] = {name: dataArgObject.name, arg: argValue, alias:alias};
  1767. }
  1768. }
  1769. // console.log(typeObject);
  1770. cfg[ele.dataset.type] = typeObject;
  1771. storage.set('cfg', cfg);
  1772. }
  1773. function updateConfigUi(e){
  1774. let name = attr(e.target, 'name');
  1775. switch (name) {
  1776. case 'mouseButton':
  1777. case 'suppressionKey':
  1778. cfg.Gesture[name] = e.target.value;
  1779. break;
  1780. case 'distanceThreshold':
  1781. case 'distanceSensitivity':
  1782. case 'timeout':
  1783. cfg.Gesture[name] = parseInt(e.target.value);
  1784. break;
  1785. case 'directions':
  1786. case 'language':
  1787. cfg[name] = e.target.value;
  1788. break;
  1789. case 'background':
  1790. case 'lineColor':
  1791. cfg.Hinter[name] = e.target.value;
  1792. attr(e.target, 'style', `background: #${e.target.value} !important;`);
  1793. break;
  1794. case 'fontSize':
  1795. case 'minLineWidth':
  1796. case 'maxLineWidth':
  1797. case 'lineGrowth':
  1798. cfg.Hinter[name] = parseFloat(parseFloat(e.target.value).toFixed(2));
  1799. break;
  1800. case 'funNotDefine':
  1801. cfg.Hinter[name] = e.target.value;
  1802. break;
  1803. case 'linktextAslink':
  1804. case 'dragInTextarea':
  1805. cfg.Drag[name] = e.target.checked;
  1806. onOff(e, e.target.checked);
  1807. break;
  1808. default:
  1809. if(name === "alias")
  1810. updateFns(e.target.parentElement);
  1811. else if(name === "fnCheckbox" || name==="fnSelect"){
  1812. formChange();
  1813. }
  1814. return;
  1815. }
  1816. storage.set('cfg', cfg);
  1817. }
  1818. function formChange(){
  1819. if(event.target.type === 'checkbox'){
  1820. event.target.value = event.target.checked;
  1821. onOff(event, event.target.checked);
  1822. updateFns(event.target.parentElement.parentElement.parentElement);
  1823. }
  1824. if(event.target.tagName === 'SELECT'){
  1825. event.target.previousElementSibling.value = event.target.value;
  1826. updateFns(event.target.parentElement.parentElement.parentElement);
  1827. }
  1828. }
  1829. function onOff(e, check) {
  1830. if (check) {
  1831. attr(e.target.nextElementSibling, 'class', 'switchOn');
  1832. } else {
  1833. attr(e.target.nextElementSibling, 'class', 'switchOff');
  1834. }
  1835. }
  1836. function addMask(){
  1837. let
  1838. w=window.innerWidth,
  1839. h=window.innerHeight,
  1840. px = 0.1*w,
  1841. string=`
  1842. <svg height="${h}" width="${w}" style="background:#00000080">
  1843. <path id="record" d="
  1844. M${50}, ${50+px} v-${px} h${px}
  1845. M${w-px-50},${50} h${px} v${px}
  1846. M${w-50}, ${h-px-50} v${px} h-${px}
  1847. M${50+px}, ${h-50} h-${px} v-${px}"
  1848. style="stroke:#fff;stroke-width:${w/50};fill:none;"></path>
  1849. <text name="clearGesture" x="100" y="${h-100}" style="font-size:${Math.floor(w/20)}px;stroke:none;fill:white;cursor:pointer;">Clear</text>
  1850. <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>
  1851. </svg>`;
  1852. let mask = document.createElement('div');
  1853. mask.id = "MPMask";
  1854. mask.innerHTML = string + '<div id="recorddingGesture"></div>';
  1855. document.body.appendChild(mask);
  1856. each(q('text[name=clearGesture], text[name=cancelGesture]'), item=>listen(item,"click",click));
  1857.  
  1858. attr(q('#MPsetting')[0], "style", "z-index:9999998 !important;");
  1859. attr(event.target, "data-flag", "captureGesture");
  1860. runtime.captureGesture = true;
  1861. }
  1862. function toggleFunsList(){
  1863. let a = q('#FunsList')[0];
  1864. if(attr(a, 'class') === "FunsListHide"){
  1865. attr(a, 'class', 'FunsListShow');
  1866. }else{
  1867. attr(a, 'class', 'FunsListHide');
  1868. }
  1869. }
  1870.  
  1871.  
  1872. return modul;
  1873. })();
  1874.  
  1875. //========⑥Run===================
  1876.  
  1877. //this addStyle is better than GM_addStyle,but not working in CSP tabs
  1878. // function addStyle(cssStr,id='MPStyle'){
  1879. // try {
  1880. // let node = document.createElement('style');
  1881. // node.id = id;
  1882. // node.textContent = cssStr;
  1883. // document.querySelector(':root').appendChild(node);
  1884. // } catch(e){}
  1885. // }
  1886. function addStyle(cssStr,id='MPStyle'){
  1887. GM_addStyle(cssStr);
  1888. }
  1889. addStyle(`
  1890. @font-face {
  1891. font-family: 'MParrow';
  1892. 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');
  1893. }
  1894. #MPcanvas{position:fixed;top:0;left:0;z-index:10000000;}
  1895. #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; }
  1896. `);
  1897. //===========update any time=========
  1898. GM_addValueChangeListener('cfg', ()=>{
  1899. GestureHandler.applySettings(cfg);
  1900. Hinter.applySettings(cfg);
  1901. });
  1902. //when close a tab, save it's url, in order to reopen it: reopenTab
  1903. window.addEventListener('unload', function() {
  1904. GM_setValue('latestTab', window.location.href);
  1905. }, false);
  1906. //used in func: closeOtherTabs
  1907. if(!GM_getValue('closeAll','')) GM_setValue('closeAll', Date());
  1908. GM_addValueChangeListener('closeAll',function(name, old_value, new_value, remote){if(remote)window.close();});
  1909. //===========update any time end=========
  1910.  
  1911. GestureHandler.applySettings(cfg);
  1912. Hinter.applySettings(cfg);
  1913. GestureHandler.enable();
  1914. Hinter.enable();
  1915.  
  1916.  
  1917. })();