Mouse Gestures like Opera

A Mouse Gestures script is the same as in the old Opera

安装此脚本
作者推荐脚本

您可能也喜欢BugMeNot Everywhere

安装此脚本
  1. // ==UserScript==
  2. // @name Mouse Gestures like Opera
  3. // @namespace https://greasyfork.org/users/37096/
  4. // @homepage https://greasyfork.org/scripts/33398/
  5. // @supportURL https://greasyfork.org/scripts/33398/feedback
  6. // @version 1.1.0
  7. // @description A Mouse Gestures script is the same as in the old Opera
  8. // @author Hồng Minh Tâm
  9. // @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
  10. // @icon https://png.icons8.com/ultraviolet/40/000000/mouse-right-click.png
  11. // @include *
  12. // @compatible chrome
  13. // @license GNU GPLv3
  14. // @grant GM_addStyle
  15. // @grant GM_openInTab
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_setValue
  18. // @grant GM_getValue
  19. // @grant window.close
  20. // @grant window.focus
  21. // @grant unsafeWindow
  22. // @noframes
  23. // ==/UserScript==
  24.  
  25. (function () {
  26. 'use strict';
  27.  
  28. GM_addStyle([
  29. '[class*="mglo-"], [class*="mglo-"] * { background-color: transparent; color: #333; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 400; line-height: 1.5; padding: 0; margin: 0; min-width: auto; min-height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }',
  30. '[class*="mglo-"]:before, [class*="mglo-"]:after, [class*="mglo-"] *:before, [class*="mglo-"] *:after { background-color: transparent; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }',
  31. /*main*/
  32. '.mglo { z-index: 10000000000; position: fixed; overflow: hidden; border: 1px solid #CCC; white-space: nowrap; font-family: sans-serif; background-color: rgba(0, 0, 0, 0.7); color: #333; border-radius: 50%; width: 400px; height: 400px; }',
  33.  
  34. '.mglo .mglo-middle, .mglo .mglo-up, .mglo .mglo-down, .mglo .mglo-left, .mglo .mglo-right { display: table; position: absolute; height: 160px; width: 160px; padding: 0; margin: 0; }',
  35. '.mglo .mglo-middle { top: 50%; left: 50%; margin-top: -20px; margin-left: -80px; width: 160px; height: 160px; text-align: center; }',
  36. '.mglo .mglo-up { top: 0; left: 50%; margin-left: -80px; }',
  37. '.mglo .mglo-down { bottom: 0; left: 50%; margin-left: -80px; }',
  38. '.mglo .mglo-left { top: 50%; left: 0; margin-top: -80px; }',
  39. '.mglo .mglo-right { top: 50%; right: 0; margin-top: -80px; }',
  40.  
  41. '.mglo .mglo-label { color: #fff; font-family: Arial,"Helvetica Neue",Helvetica,sans-serif; font-weight: 700; font-size: 16px; text-transform: none; letter-spacing: normal; white-space: pre-wrap; padding: 0; margin: 0; -webkit-transition: all .2s; -moz-transition: all .2s; transition: all .2s; line-height: 22px; }',
  42. '.mglo .mglo-up > .mglo-label { display: table-cell; vertical-align: bottom; text-align: center; padding-bottom: 50px; }',
  43. '.mglo .mglo-down > .mglo-label { display: table-cell; vertical-align: top; text-align: center; padding-top: 50px; }',
  44. '.mglo .mglo-left > .mglo-label { display: table-cell; vertical-align: middle; text-align: right; padding-right: 50px; }',
  45. '.mglo .mglo-right > .mglo-label { display: table-cell; vertical-align: middle; text-align: left; padding-left: 50px; }',
  46.  
  47. '.mglo .mglo-icon { position: absolute; width: initial; display: initial; }',
  48. '.mglo .mglo-middle > .mglo-icon { position: initial; }',
  49. '.mglo .mglo-up > .mglo-icon { bottom: 0; left: 50%; margin-left: -20px; }',
  50. '.mglo .mglo-down > .mglo-icon { top: 0; left: 50%; margin-left: -20px; }',
  51. '.mglo .mglo-left > .mglo-icon { top: 50%; right: 0; margin-top: -20px; }',
  52. '.mglo .mglo-right > .mglo-icon { top: 50%; left: 0; margin-top: -20px; }',
  53.  
  54. '.mglo .active > .mglo-label { background-color: transparent; color: #ffff00; }',
  55. '.mglo .mglo-up.active > .mglo-label { padding-bottom: 10px; }',
  56. '.mglo .mglo-down.active > .mglo-label { padding-top: 10px; }',
  57. '.mglo .mglo-left.active > .mglo-label { padding-right: 10px; }',
  58. '.mglo .mglo-right.active > .mglo-label { padding-left: 10px; }',
  59. '.mglo .active > .mglo-icon { display: none; }',
  60.  
  61. '.mglo.hide, .mglo .hide { display: none; }',
  62. /*list icon*/
  63. '.mglo-list-icon { line-height: 0; margin-bottom: 10px; }',
  64. '.mglo-list-icon > .mglo-icon { width: 30px; height: 30px; -webkit-user-drag: none; user-select: none; }',
  65. '.mglo-list-icon-group > label { display: inline-block; margin-bottom: 5px; }',
  66. '.mglo-list-icon-group.inline .mglo-list-icon { display: inline-block; }',
  67. /*dialog*/
  68. '.mglo-dialog { z-index: 9999999999; padding-top: 30px; padding-bottom: 30px; position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0,0,0,0.5); }',
  69. '.mglo-dialog-content { max-height: 100%; min-height: 200px; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -moz-box-orient: vertical; -webkit-flex-flow: column; -ms-flex-direction: column; flex-flow: column; margin: auto; background-color: #fff; position: relative; outline: 0; width: 600px; }',
  70. '.mglo-dialog-form { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-box-orient: vertical; -moz-box-orient: vertical; -webkit-flex-flow: column; -ms-flex-direction: column; flex-flow: column; }',
  71. '.mglo-dialog-header { color: #fff; background-color: #2196F3; padding: 15px; font-weight: 700; position: relative; }',
  72. '.mglo-dialog-body { overflow: auto; padding: 15px; }',
  73. '.mglo-dialog-footer { background-color: #f9f9f9; border-top: 1px solid #ddd; padding: 8px 15px; text-align: right; }',
  74. '.mglo-dialog-footer > button + button { margin-left: 8px; }',
  75. '.mglo-dialog-footer:empty { display: none; }',
  76. '.mglo-dialog-footer:before, .mglo-dialog-footer:after { content: ""; display: table; clear: both; }',
  77. '.mglo-dialog .mglo-close { position: absolute; right: 0; top: 0; bottom: 0; padding: 0 20px; border: none; box-shadow: none; border-radius: 0; outline: 0; text-decoration: none; color: inherit; background-color: inherit; font-size: 24px; font-weight: 700; }',
  78. '.mglo-dialog .mglo-close:hover { color: #fff; background-color: #f44336; }',
  79. /*form*/
  80. '.mglo-form-group > label { display: inline-block; margin-bottom: 5px; }',
  81. '.mglo-form-control { padding: 5px 10px; color: #333; background-color: #fff; border: 1px solid #ccc; width: 100%; display: block; margin-bottom: 10px }',
  82. '.mglo-form-group.inline .mglo-form-control { display: inline-block; margin-left: 8px; width: auto; }',
  83. '.mglo-form-control[disabled] { background-color: #eee; color: #888 }',
  84.  
  85. '.mglo-form-check { display: block; margin-bottom: 10px; }',
  86. '.mglo-form-group.inline .mglo-form-check { display: inline-block; }',
  87. '.mglo-form-group.inline .mglo-form-check + .mglo-form-check { margin-left: 10px; }',
  88. '.mglo-form-check-label { position: relative; padding: 0; margin: 0; display: inline-block; }',
  89. '.mglo-form-check-input { display: none !important; }',
  90. 'input.mglo-form-check-input + span, input.mglo-form-check-input + span { padding: 0; margin: 0; }',
  91. '.mglo-form-check-input + span:before { position: relative; top: 5px; display: inline-block; width: 20px; height: 20px; content: ""; border: 2px solid #c0c0c0; margin-right: 8px; background-color: #fff; }',
  92. '.mglo-form-check-input:checked + span:before { border-color: #3e97eb; }',
  93. '.mglo-form-check-input:checked + span:after { content: ""; position: absolute; }',
  94. '.mglo-form-check-input[type="checkbox"] +span:before { border-radius: 2px; }',
  95. '.mglo-form-check-input[type="checkbox"]:checked + span:before { background: #3e97eb; }',
  96. '.mglo-form-check-input[type="checkbox"]:checked + span:after { top: 8px; left: 7px; width: 6px; height: 12px; transform: rotate(45deg); border: 2px solid #fff; border-top: 0; border-left: 0; }',
  97. '.mglo-form-check-input[type="radio"] +span:before { border-radius: 50%; }',
  98. '.mglo-form-check-input[type="radio"]:checked + span:after { top: 10px; left: 5px; width: 10px; height: 10px; border-radius: 50%; background: #3e97eb; }',
  99.  
  100. '.mglo-form-no-label, .mglo-form-no-label input, .mglo-form-no-label select { margin-bottom: 0; }',
  101. '.mglo-form-no-label .mglo-form-check-input + span:before { margin-right: 0; }',
  102. '.mglo-form-no-label .mglo-form-check-label { height: 30px; }',
  103. '.mglo-form-no-label .mglo-list-icon { margin-bottom: 0; }',
  104. /*button*/
  105. '.mglo-btn { padding: 5px 10px; color: #333; background-color: #fff; border: 1px solid #ccc; }',
  106. '.mglo-btn:hover { color: #333; background-color: #e6e6e6; }',
  107. '.mglo-btn:active { background-color: #c6c6c6; }',
  108. '.mglo-btn.blue { color: #fff; background-color: #2196F3; border-color: #2196F3; }',
  109. '.mglo-btn.red { color: #fff; background-color: #f44336; border-color: #f44336; }',
  110. '.mglo-btn.left { float: left; }',
  111. '.mglo-btn.right { float: right; }',
  112. /*inputmg*/
  113. '.mglo-form-group.mglo-form-group-record { display: flex; }',
  114. '.mglo-form-group.mglo-form-group-record .mglo-btn.mglo-btn-record:before { display: block; content: ""; background-color: #808080; width: 12px; height: 12px; border-radius: 50%; transition: 0.2s; }',
  115. '.mglo-form-group.mglo-form-group-record .mglo-btn.mglo-btn-record:hover:before { background-color: #f44336; transition: 0.2s; }',
  116. '.mglo-form-group.mglo-form-group-record .mglo-form-control { text-transform: uppercase; font-weight: 700; }',
  117. '.mglo-record-backdrop { z-index: 9999999999; position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0,0,0,0.5); }',
  118. '.mglo-record-backdrop .mglo-list-icon { position: absolute; bottom: 0; left: 0; right: 0;text-align: center; background-color: rgba(0,0,0,0.5); }',
  119. '.mglo-record-backdrop .mglo-list-icon > .mglo-icon { margin-top: 20px; margin-bottom: 20px; }',
  120. /*table*/
  121. '.mglo-table { border: 1px solid #c0c0c0; border-collapse: collapse; border-spacing: 0; width: 100%; }',
  122. '.mglo-table th { font-weight: 700; }',
  123. '.mglo-table tr { border-bottom: 1px solid #c0c0c0; }',
  124. '.mglo-table td, .mglo-table th { padding: 8px; vertical-align: top; }',
  125. '.mglo-table tr:nth-child(even) { background-color: #f1f1f1; }',
  126. /*item*/
  127. '.mglo-item { padding: 8px; border: 1px solid #ccc; }',
  128. '.mglo-item + .mglo-item { margin-top: 5px; }',
  129. '.mglo-item>*:last-child, .mglo-item>*:last-child>*:last-child { margin-bottom: 0; }'
  130. ].join('\n'));
  131.  
  132. var varw = (function (context) {
  133. return function (varName, varValue, setEvent) {
  134. var value = varValue;
  135. Object.defineProperty(context, varName, {
  136. get: function () {
  137. return value;
  138. },
  139. set: function (v) {
  140. value = v;
  141. if (setEvent) {
  142. setEvent(v);
  143. }
  144. }
  145. });
  146. };
  147. })(unsafeWindow);
  148.  
  149. varw('isRecord', false, function (value) {
  150. recordMG(value);
  151. });
  152.  
  153. var SENSITIVITY = 20;
  154. var startX, startY;
  155. var gesture = '';
  156. var gestures;
  157. var values;
  158. var preventContextMenu = false;
  159. var mouseDownTriggered = false;
  160. var timeoutDelay;
  161. var dialogSetting;
  162. var icon = {
  163. up: getIcons8('ultraviolet', 'up', 40, '000000'),
  164. down: getIcons8('ultraviolet', 'down', 40, '000000'),
  165. left: getIcons8('ultraviolet', 'left', 40, '000000'),
  166. right: getIcons8('ultraviolet', 'right', 40, '000000'),
  167. mouseRightClick: getIcons8('ultraviolet', 'mouse-right-click', 40, '000000')
  168. };
  169. var $contentSetting = $('<div/>');
  170. var $buttonReset = $('<button/>', {
  171. class: 'mglo-btn',
  172. type: 'reset',
  173. text: 'Reset'
  174. });
  175. var $buttonSave = $('<button/>', {
  176. class: 'mglo-btn blue',
  177. text: 'Save'
  178. });
  179. var $buttonFooterSettings = [$buttonReset, $buttonSave];
  180. var $currentInputMG;
  181. var $recordBackdrop = $('<div/>', {
  182. class: 'mglo-record-backdrop'
  183. });
  184. var gestureStrings = {
  185. up: 'u',
  186. down: 'd',
  187. left: 'l',
  188. right: 'r'
  189. };
  190. var defaultActions = {
  191. closeTab: {
  192. label: 'Close tab',
  193. category: 'Tab',
  194. fn: function () {
  195. window.top.close();
  196. }
  197. },
  198. newTab: {
  199. label: 'New tab',
  200. category: 'Tab',
  201. fn: function () {
  202. defaultActions.openInNewTab.fn();
  203. }
  204. },
  205. duplicateTab: {
  206. label: 'Duplicate tab',
  207. category: 'Tab',
  208. fn: function () {
  209. defaultActions.openInNewTab.fn(unsafeWindow.location.href);
  210. }
  211. },
  212. openInNewTab: {
  213. label: 'Open link in new tab',
  214. category: 'Tab',
  215. fn: function (link) {
  216. GM_openInTab(link, false);
  217. },
  218. onLink: true
  219. },
  220. openInNewBackgroundTab: {
  221. label: 'Open link in new background tab',
  222. category: 'Tab',
  223. fn: function (link) {
  224. GM_openInTab(link, true);
  225. },
  226. onLink: true
  227. },
  228. scrollUp: {
  229. label: 'Scroll up',
  230. category: 'Scroll',
  231. fn: function () {
  232. $('html, body').animate({
  233. scrollTop: '-=600'
  234. }, 'slow');
  235. }
  236. },
  237. scrollDown: {
  238. label: 'Scroll down',
  239. category: 'Scroll',
  240. fn: function () {
  241. $('html, body').animate({
  242. scrollTop: '+=600'
  243. }, 'slow');
  244. }
  245. },
  246. scrollToTop: {
  247. label: 'Scroll to top',
  248. category: 'Scroll',
  249. fn: function () {
  250. $('html, body').animate({
  251. scrollTop: 0
  252. }, 'slow');
  253. }
  254. },
  255. scrollToBottom: {
  256. label: 'Scroll to bottom',
  257. category: 'Scroll',
  258. fn: function () {
  259. $('html, body').animate({
  260. scrollTop: $(document).height()
  261. }, 'slow');
  262. }
  263. },
  264. scrollUpOnElement: {
  265. label: 'Scroll up on element',
  266. category: 'Scroll',
  267. fn: function () {
  268. var scrollChange = (values.$vScrollBarUp.is('html, body') ? $(window).height() : values.$vScrollBarUp.innerHeight()) * 5 / 7;
  269. values.$vScrollBarUp.animate({
  270. scrollTop: '-=' + scrollChange
  271. }, 'slow');
  272. }
  273. },
  274. scrollDownOnElement: {
  275. label: 'Scroll down on element',
  276. category: 'Scroll',
  277. fn: function () {
  278. var scrollChange = (values.$vScrollBarDown.is('html, body') ? $(window).height() : values.$vScrollBarDown.innerHeight()) * 5 / 7;
  279. values.$vScrollBarDown.animate({
  280. scrollTop: '+=' + scrollChange
  281. }, 'slow');
  282. }
  283. },
  284. back: {
  285. label: 'Back',
  286. category: 'Navigation',
  287. fn: function () {
  288. unsafeWindow.history.back();
  289. }
  290. },
  291. forward: {
  292. label: 'Forward',
  293. category: 'Navigation',
  294. fn: function () {
  295. unsafeWindow.history.forward();
  296. }
  297. },
  298. reload: {
  299. label: 'Reload',
  300. category: 'Load',
  301. fn: function () {
  302. unsafeWindow.location.reload();
  303. }
  304. },
  305. reloadWithoutCache: {
  306. label: 'Reload without cache',
  307. category: 'Load',
  308. fn: function () {
  309. unsafeWindow.location.reload(true);
  310. }
  311. },
  312. // settingsMGLO: {
  313. // label: 'Settings Mouse Gestures like Opera',
  314. // category: 'Other',
  315. // fn: function () {
  316. // showDialogSetting();
  317. // }
  318. // },
  319. };
  320. var defaultGestures = {
  321. u: {
  322. gesture: 'scrollUpOnElement',
  323. },
  324. d: {
  325. gesture: 'scrollDownOnElement',
  326. },
  327. // u: {
  328. // gesture: 'scrollUp',
  329. // },
  330. // d: {
  331. // gesture: 'scrollDown',
  332. // },
  333. l: {
  334. gesture: 'back',
  335. },
  336. r: {
  337. gesture: 'forward',
  338. },
  339. ud: {
  340. gesture: 'reload',
  341. },
  342. ur: {
  343. gesture: 'newTab',
  344. },
  345. du: {
  346. gesture: 'duplicateTab',
  347. },
  348. dl: {
  349. gesture: 'openInNewTab',
  350. },
  351. dr: {
  352. gesture: 'closeTab',
  353. },
  354. // lu: {
  355. // gesture: 'scrollUpOnElement',
  356. // },
  357. // ld: {
  358. // gesture: 'scrollDownOnElement',
  359. // },
  360. ru: {
  361. gesture: 'scrollToTop',
  362. },
  363. rd: {
  364. gesture: 'scrollToBottom',
  365. },
  366. udu: {
  367. gesture: 'reloadWithoutCache',
  368. },
  369. dld: {
  370. gesture: 'openInNewBackgroundTab',
  371. },
  372. // dudu: {
  373. // gesture: 'settingsMGLO',
  374. // }
  375. };
  376.  
  377. function getIcons8(style, id, size, color) {
  378. return 'https://png.icons8.com/' + style + '/' + size + '/' + color + '/' + id + '.png';
  379. }
  380.  
  381. // function setGestures() {
  382. // GM_setValue('gestures', gestures);
  383. // }
  384.  
  385. // function getGestures() {
  386. // return GM_getValue('gestures');
  387. // }
  388.  
  389. // function loadGestures() {
  390. // var valueGestures = getGestures();
  391. // if (valueGestures) {
  392. // gestures = valueGestures;
  393. // } else {
  394. // resetGestures();
  395. // }
  396. // }
  397.  
  398. // function resetGestures() {
  399. // gestures = defaultGestures;
  400. // setGestures();
  401. // }
  402.  
  403. // loadGestures();
  404. gestures = defaultGestures;
  405.  
  406. var $mouseGestures = $('<div/>', {
  407. class: 'mglo hide'
  408. }).appendTo(document.body);
  409. var widthMouseGestures = $mouseGestures.width();
  410. var heightMouseGestures = $mouseGestures.height();
  411. var halfWidthMouseGestures = widthMouseGestures / 2;
  412. var halfHeightMouseGestures = heightMouseGestures / 2;
  413.  
  414. var $up = $('<div/>', {
  415. class: 'mglo-up'
  416. }).appendTo($mouseGestures);
  417. var $upIcon = $('<img/>', {
  418. class: 'mglo-icon',
  419. src: icon.up
  420. }).appendTo($up);
  421. var $upLabel = $('<div/>', {
  422. class: 'mglo-label'
  423. }).appendTo($up);
  424.  
  425. var $down = $('<div/>', {
  426. class: 'mglo-down'
  427. }).appendTo($mouseGestures);
  428. var $downIcon = $('<img/>', {
  429. class: 'mglo-icon',
  430. src: icon.down
  431. }).appendTo($down);
  432. var $downLabel = $('<div/>', {
  433. class: 'mglo-label'
  434. }).appendTo($down);
  435.  
  436. var $left = $('<div/>', {
  437. class: 'mglo-left'
  438. }).appendTo($mouseGestures);
  439. var $leftIcon = $('<img/>', {
  440. class: 'mglo-icon',
  441. src: icon.left
  442. }).appendTo($left);
  443. var $leftLabel = $('<div/>', {
  444. class: 'mglo-label'
  445. }).appendTo($left);
  446.  
  447. var $right = $('<div/>', {
  448. class: 'mglo-right'
  449. }).appendTo($mouseGestures);
  450. var $rightIcon = $('<img/>', {
  451. class: 'mglo-icon',
  452. src: icon.right
  453. }).appendTo($right);
  454. var $rightLabel = $('<div/>', {
  455. class: 'mglo-label'
  456. }).appendTo($right);
  457.  
  458. var $middle = $('<div/>', {
  459. class: 'mglo-middle'
  460. }).appendTo($mouseGestures);
  461. var $middleIcon = $('<img/>', {
  462. class: 'mglo-icon',
  463. src: icon.mouseRightClick
  464. }).appendTo($middle);
  465. var $middleLabel = $('<div/>', {
  466. class: 'mglo-label'
  467. }).appendTo($middle);
  468.  
  469. var ListIcon = (function () {
  470. function ListIcon(label, attribute) {
  471. this.gesture = '';
  472. var $control = $('<div/>', {
  473. class: 'mglo-list-icon-group'
  474. });
  475. if (typeof label === 'undefined') {
  476. $control.addClass('mglo-form-no-label');
  477. } else {
  478. var $label = $('<label/>');
  479. $label.text(label);
  480. $label.appendTo($control);
  481. }
  482. var $listIcon = $('<div/>', attribute);
  483. $listIcon.addClass('mglo-list-icon');
  484. $listIcon.appendTo($control);
  485. this.$listIcon = $listIcon;
  486. this.$element = $control;
  487. this.setGesture(attribute.gesture);
  488. }
  489. ListIcon.prototype = {
  490. setGesture: function (gesture) {
  491. var _this = this;
  492. this.gesture = gesture;
  493. this.$listIcon.empty();
  494. this.$listIcon.data('data-gesture', gesture);
  495. gesture.split('').forEach(function (c) {
  496. switch (c) {
  497. case gestureStrings.up:
  498. $upIcon.clone().appendTo(_this.$listIcon);
  499. break;
  500. case gestureStrings.down:
  501. $downIcon.clone().appendTo(_this.$listIcon);
  502. break;
  503. case gestureStrings.left:
  504. $leftIcon.clone().appendTo(_this.$listIcon);
  505. break;
  506. case gestureStrings.right:
  507. $rightIcon.clone().appendTo(_this.$listIcon);
  508. break;
  509. }
  510. });
  511. }
  512. };
  513. return ListIcon;
  514. })();
  515.  
  516. var Form = {
  517. Control: (function () {
  518. function Control(type, label, attribute, event) {
  519. if (typeof attribute !== 'object') {
  520. attribute = {};
  521. }
  522. if (typeof attribute.id === 'undefined') {
  523. attribute.id = 'mglo-' + type + '-' + new Date().getTime();
  524. }
  525. var $control = $('<div/>', {
  526. class: 'mglo-form-group'
  527. });
  528. if (typeof label === 'undefined') {
  529. $control.addClass('mglo-form-no-label');
  530. } else {
  531. var $label = $('<label/>', {
  532. for: attribute.id
  533. });
  534. $label.text(label);
  535. $label.appendTo($control);
  536. }
  537. var $input = $('<input/>', attribute);
  538. $input.attr({
  539. type: type,
  540. class: 'mglo-form-control'
  541. });
  542. $input.appendTo($control);
  543. this.label = label;
  544. this.id = attribute.id;
  545. this.$input = $input;
  546. this.$element = $control;
  547. if (type === 'number') {
  548. $input.on('input.mglo', function (e) {
  549. e.target.value = parseInt(e.target.value) ? e.target.value.replace(/^0+/, '') : (this.min || 0);
  550. event(e);
  551. });
  552. } else {
  553. $input.on('input.mglo', event);
  554. }
  555. }
  556. Control.prototype = {
  557. oninput: function (event) {
  558. this.$input.on('input.mglo', function (e) {
  559. e.target.value = parseInt(e.target.value) ? e.target.value.replace(/^0+/, '') : (this.min || 0);
  560. event(e);
  561. });
  562. }
  563. };
  564. return Control;
  565. }()),
  566. Select: (function () {
  567. function Select(label, attribute, event) {
  568. var items = [],
  569. itemElements = [],
  570. value;
  571. if (typeof attribute !== 'object') {
  572. attribute = {};
  573. }
  574. if (typeof attribute.id === 'undefined') {
  575. attribute.id = 'mglo-select-' + new Date().getTime();
  576. }
  577. if (typeof attribute.items !== 'undefined') {
  578. items = $.extend(true, [], attribute.items);
  579. delete attribute.items;
  580. }
  581. if (typeof attribute.value !== 'undefined') {
  582. value = attribute.value;
  583. }
  584. var $control = $('<div/>', {
  585. class: 'mglo-form-group',
  586. });
  587. if (typeof label === 'undefined') {
  588. $control.addClass('mglo-form-no-label');
  589. } else {
  590. var $label = $('<label/>', {
  591. for: attribute.id
  592. });
  593. $label.text(label);
  594. $label.appendTo($control);
  595. }
  596. var $select = $('<select/>', attribute);
  597. $select.attr('class', 'mglo-form-control');
  598. $select.appendTo($control);
  599. if (!Array.isArray(items)) {
  600. items = [items];
  601. }
  602. for (var i = 0; i < items.length; i++) {
  603. var text = items[i].label;
  604. var $item = $('<option/>', items[i]);
  605. $item.text(text);
  606. if (value === items[i].value) {
  607. $item.prop('selected', true);
  608. }
  609. if (typeof items[i].optgroup !== 'undefined') {
  610. if ($select.find('optgroup[label="' + items[i].optgroup + '"]').size()) {
  611. $select.find('optgroup[label="' + items[i].optgroup + '"]').append($item);
  612. } else {
  613. var $optgroup = $('<optgroup/>', {
  614. label: items[i].optgroup
  615. });
  616. $optgroup.append($item);
  617. $optgroup.appendTo($select);
  618. }
  619. } else {
  620. $item.appendTo($select);
  621. }
  622. itemElements.push($item);
  623. }
  624. this.label = label;
  625. this.id = attribute.id;
  626. this.$select = $select;
  627. this.$element = $control;
  628. this.items = itemElements;
  629. $select.on('change.mglo', event);
  630. }
  631. Select.prototype = {
  632. onchange: function (event) {
  633. this.$select.on('change.mglo', event);
  634. }
  635. };
  636. return Select;
  637. }()),
  638. CheckInput: (function () {
  639. function CheckInput(type, label, attribute, event) {
  640. if (typeof attribute !== 'object') {
  641. attribute = {};
  642. }
  643. if (typeof attribute.id === 'undefined') {
  644. attribute.id = 'mglo-' + type + '-' + new Date().getTime();
  645. }
  646. var $checkInput = $('<div/>', {
  647. class: 'mglo-form-check',
  648. });
  649. if (typeof label === 'undefined') {
  650. $checkInput.addClass('mglo-form-no-label');
  651. }
  652. var $label = $('<label/>', {
  653. class: 'mglo-form-check-label'
  654. }).appendTo($checkInput);
  655. var $input = $('<input/>', attribute);
  656. $input.attr({
  657. type: type,
  658. class: 'mglo-form-check-input'
  659. });
  660. $input.appendTo($label);
  661. var $text = $('<span/>').text(label);
  662. $text.appendTo($label);
  663. this.label = label;
  664. this.id = attribute.id;
  665. this.$input = $input;
  666. this.$element = $checkInput;
  667. $input.on('change.mglo', event);
  668. }
  669. CheckInput.prototype = {
  670. onchange: function (event) {
  671. this.$input.on('change.mglo', event);
  672. },
  673. };
  674. CheckInput.createGroup = function (name, checkInputs) {
  675. var $checkInputGroup = $('<div/>', {
  676. class: 'mglo-form-group',
  677. id: name
  678. });
  679. for (var i = 0; i < checkInputs.length; i++) {
  680. var checkInput = checkInputs[i];
  681. checkInput.$input.name = name;
  682. $checkInputGroup.append(checkInput.$element);
  683. }
  684. return $checkInputGroup;
  685. };
  686. return CheckInput;
  687. }()),
  688. InputMG: (function () {
  689. function InputMG(label, attribute) {
  690. if (typeof attribute !== 'object') {
  691. attribute = {};
  692. }
  693. if (typeof attribute.id === 'undefined') {
  694. attribute.id = 'mglo-input-mg-' + new Date().getTime();
  695. }
  696. var $control = $('<div/>', {
  697. class: 'mglo-form-group mglo-form-group-record'
  698. });
  699. if (typeof label === 'undefined') {
  700. $control.addClass('mglo-form-no-label');
  701. } else {
  702. var $label = $('<label/>', {
  703. for: attribute.id
  704. });
  705. $label.text(label);
  706. $label.appendTo($control);
  707. }
  708. var $input = $('<input/>', attribute);
  709. $input.attr({
  710. type: 'text',
  711. class: 'mglo-form-control'
  712. });
  713. $input.appendTo($control);
  714. var $recordButton = $('<button/>', {
  715. class: 'mglo-btn mglo-btn-record'
  716. });
  717. $recordButton.on('click.mglo', function (e) {
  718. e.preventDefault();
  719. $currentInputMG = $(this).parent().find('input.mglo-form-control');
  720. isRecord = true;
  721. });
  722. $recordButton.appendTo($control);
  723. this.label = label;
  724. this.id = attribute.id;
  725. this.$input = $input;
  726. this.$element = $control;
  727. this.$recordButton = $recordButton;
  728. $input.on('keypress.mglo', function (e) {
  729. var key = e.keyCode;
  730. key = String.fromCharCode(key);
  731. var regex = /(\b(?:([UDLRudlr])(?!\2{1}))+\b)/g;
  732. if (!regex.test(key)) {
  733. e.returnValue = false;
  734. if (e.preventDefault) e.preventDefault();
  735. }
  736. }).on('input.mglo', function (e) {
  737. $(this).val(function (i, val) {
  738. return val.toLowerCase();
  739. });
  740. });
  741. }
  742. return InputMG;
  743. }()),
  744. };
  745.  
  746. var recordListIcon = new ListIcon(undefined, {
  747. gesture: gesture
  748. });
  749.  
  750. function mouseMoveMG(e) {
  751. if (startY - e.clientY > 10 || e.clientY - startY > 10 || startX - e.clientX > 10 || e.clientX - startX > 10) {
  752. preventContextMenu = false;
  753. if (mouseDownTriggered) {
  754. mouseDownTriggered = false;
  755. } else {
  756. clearTimeout(timeoutDelay);
  757. showMG(e);
  758. checkMG(e);
  759. }
  760. }
  761. }
  762.  
  763. function mouseDownMG(e, data) {
  764. e = data || e;
  765. if (e.which === 3) {
  766. getValues(e);
  767. preventContextMenu = false;
  768. mouseDownTriggered = true;
  769. startX = e.clientX;
  770. startY = e.clientY;
  771. gesture = '';
  772. loadMG();
  773. timeoutDelay = setTimeout(function () {
  774. showMG(e);
  775. }, 500);
  776. $(document).on('mousemove.mglo', mouseMoveMG);
  777. }
  778. }
  779.  
  780. function mouseUpMG(e) {
  781. clearTimeout(timeoutDelay);
  782. $(document).off('mousemove.mglo');
  783. if (isRecord === false && checkTypeItemMG('link', gesture, true)) {
  784. switch (gesture.slice(-1)) {
  785. case gestureStrings.up:
  786. $down.addClass('hide');
  787. $left.addClass('hide');
  788. $right.addClass('hide');
  789. break;
  790. case gestureStrings.down:
  791. $up.addClass('hide');
  792. $left.addClass('hide');
  793. $right.addClass('hide');
  794. break;
  795. case gestureStrings.left:
  796. $up.addClass('hide');
  797. $down.addClass('hide');
  798. $right.addClass('hide');
  799. break;
  800. case gestureStrings.right:
  801. $up.addClass('hide');
  802. $down.addClass('hide');
  803. $left.addClass('hide');
  804. break;
  805. }
  806. $mouseGestures.delay(300).addClass('hide');
  807. } else {
  808. $mouseGestures.addClass('hide');
  809. if (isRecord === true && gesture) {
  810. isRecord = false;
  811. }
  812. }
  813. gesture = '';
  814. }
  815.  
  816. function contextMenuMG(e) {
  817. if (preventContextMenu) e.preventDefault();
  818. }
  819.  
  820. $(document).on('mousedown.mglo', mouseDownMG)
  821. .on('mouseup.mglo', mouseUpMG)
  822. .on('contextmenu.mglo', contextMenuMG);
  823.  
  824. function showMG(e) {
  825. preventContextMenu = true;
  826. var mouseX = e.pageX - $(window).scrollLeft();
  827. var mouseY = e.pageY - $(window).scrollTop();
  828. $mouseGestures.css({
  829. left: mouseX - halfWidthMouseGestures,
  830. top: mouseY - halfHeightMouseGestures
  831. }).stop(true, true);
  832. $mouseGestures.removeClass('hide');
  833. }
  834.  
  835. function checkMG(e) {
  836. checkMove(startY - e.clientY, gestureStrings.up, e);
  837. checkMove(e.clientY - startY, gestureStrings.down, e);
  838. checkMove(startX - e.clientX, gestureStrings.left, e);
  839. checkMove(e.clientX - startX, gestureStrings.right, e);
  840. if (isRecord === true) {
  841. recordListIcon.setGesture(gesture);
  842. }
  843. }
  844.  
  845. function checkMove(p, t, e) {
  846. if (p >= SENSITIVITY) {
  847. startX = e.clientX;
  848. startY = e.clientY;
  849. if (gesture.slice(-1) != t) {
  850. gesture += t;
  851. loadMG();
  852. }
  853. }
  854. }
  855.  
  856. function loadMG() {
  857. if (checkTypeItemMG('link', gesture + gestureStrings.up)) {
  858. $up.removeClass('active hide');
  859. $upLabel.text(getGesture(gesture + gestureStrings.up, 'label'));
  860. } else {
  861. $up.addClass('hide');
  862. }
  863. if (checkTypeItemMG('link', gesture + gestureStrings.down)) {
  864. $down.removeClass('active hide');
  865. $downLabel.text(getGesture(gesture + gestureStrings.down, 'label'));
  866. } else {
  867. $down.addClass('hide');
  868. }
  869. if (checkTypeItemMG('link', gesture + gestureStrings.left)) {
  870. $left.removeClass('active hide');
  871. $leftLabel.text(getGesture(gesture + gestureStrings.left, 'label'));
  872. } else {
  873. $left.addClass('hide');
  874. }
  875. if (checkTypeItemMG('link', gesture + gestureStrings.right)) {
  876. $right.removeClass('active hide');
  877. $rightLabel.text(getGesture(gesture + gestureStrings.right, 'label'));
  878. } else {
  879. $right.addClass('hide');
  880. }
  881.  
  882. if (checkTypeItemMG('link', gesture)) {
  883. switch (gesture.slice(-1)) {
  884. case gestureStrings.up:
  885. $up.removeClass('hide').addClass('active');
  886. break;
  887. case gestureStrings.down:
  888. $down.removeClass('hide').addClass('active');
  889. break;
  890. case gestureStrings.left:
  891. $left.removeClass('hide').addClass('active');
  892. break;
  893. case gestureStrings.right:
  894. $right.removeClass('hide').addClass('active');
  895. break;
  896. }
  897. }
  898. }
  899.  
  900. function recordMG(isTurn) {
  901. if (isTurn === true) {
  902. if (dialogSetting && dialogSetting.isShow) {
  903. $recordBackdrop.insertAfter(dialogSetting.$dialog);
  904. } else {
  905. $recordBackdrop.appendTo(document.body);
  906. }
  907. recordListIcon.$element.appendTo($recordBackdrop);
  908. } else if (isTurn === false && gesture) {
  909. $currentInputMG.val(gesture);
  910. recordListIcon.setGesture('');
  911. $recordBackdrop.remove();
  912. }
  913. }
  914.  
  915. function getValues(e) {
  916. values = {};
  917. values.$target = $(e.target);
  918. if (values.$target.closest('a').length) {
  919. values.link = values.$target.closest('a').prop('href');
  920. }
  921. values.$vScrollBar = values.$target.vScrollBarParent();
  922. values.$vScrollBarUp = values.$target.vScrollBarParent('up');
  923. values.$vScrollBarDown = values.$target.vScrollBarParent('down');
  924. }
  925.  
  926. function getGesture(gesture, prototype) {
  927. if (typeof prototype !== 'undefined') {
  928. if (gestures[gesture].gesture !== 'custom') {
  929. return defaultActions[gestures[gesture].gesture][prototype];
  930. } else {
  931. return gestures[gesture].custom[prototype];
  932. }
  933. } else {
  934. if (gestures[gesture].gesture !== 'custom') {
  935. return defaultActions[gestures[gesture].gesture];
  936. } else {
  937. return gestures[gesture].custom;
  938. }
  939. }
  940. }
  941.  
  942. function checkTypeItemMG(type, gesture, runFunction) {
  943. if (typeof runFunction === 'undefined') {
  944. runFunction = false;
  945. }
  946. if (gestures[gesture]) {
  947. if (getGesture(gesture, 'on' + type.toCapitalize().replace(' ', ''))) {
  948. if (values[type]) {
  949. if (runFunction === true) {
  950. getGesture(gesture, 'fn')(values[type]);
  951. }
  952. return true;
  953. } else {
  954. return false;
  955. }
  956. } else {
  957. if (runFunction === true) {
  958. getGesture(gesture, 'fn')();
  959. }
  960. return true;
  961. }
  962. } else {
  963. return false;
  964. }
  965. }
  966.  
  967. String.prototype.toCapitalize = function () {
  968. return this.replace(/(\b)([a-zA-Z])/g, function (m) {
  969. return m.toUpperCase();
  970. });
  971. };
  972.  
  973. var Dialog = (function () {
  974. function Dialog(title, $content, $buttonFooters, isForm, id) {
  975. if (typeof id === 'undefined') {
  976. id = 'dialog-' + new Date().getTime();
  977. }
  978. if (typeof isForm === 'undefined') {
  979. isForm = false;
  980. }
  981. this.title = title;
  982. this.$content = $content.clone(true, true);
  983. this.id = id;
  984. this.isShow = false;
  985. this.status = 'create';
  986. var $dialog = $('<div/>', {
  987. class: 'mglo-dialog',
  988. id: this.id
  989. });
  990. this.$dialog = $dialog;
  991. $dialog.get(0).onclick = this.close.bind(this);
  992. var $dialogContent = $('<div/>', {
  993. class: 'mglo-dialog-content'
  994. });
  995. $dialogContent.appendTo($dialog);
  996.  
  997. var $dialogForm = $('<form/>', {
  998. class: 'mglo-dialog-form'
  999. });
  1000. if (isForm === true) {
  1001. $dialogForm.appendTo($dialogContent);
  1002. }
  1003.  
  1004. var $dialogHeader = $('<div/>', {
  1005. class: 'mglo-dialog-header'
  1006. });
  1007. $dialogHeader.append(this.title);
  1008. if (isForm === true) {
  1009. $dialogHeader.appendTo($dialogForm);
  1010. } else {
  1011. $dialogHeader.appendTo($dialogContent);
  1012. }
  1013. var $buttonClose = $('<button/>', {
  1014. class: 'mglo-close'
  1015. });
  1016. $buttonClose.html('\u00d7');
  1017. $buttonClose.get(0).onclick = this.close.bind(this);
  1018. $buttonClose.appendTo($dialogHeader);
  1019.  
  1020.  
  1021. var $dialogBody = $('<div/>', {
  1022. class: 'mglo-dialog-body'
  1023. });
  1024. $dialogBody.append(this.$content);
  1025. if (isForm === true) {
  1026. $dialogBody.appendTo($dialogForm);
  1027. } else {
  1028. $dialogBody.appendTo($dialogContent);
  1029. }
  1030.  
  1031. var $dialogFooter = $('<div/>', {
  1032. class: 'mglo-dialog-footer'
  1033. });
  1034. if (isForm === true) {
  1035. $dialogFooter.appendTo($dialogForm);
  1036. } else {
  1037. $dialogFooter.appendTo($dialogContent);
  1038. }
  1039. if (typeof $buttonFooters !== 'undefined') {
  1040. if ($buttonFooters instanceof jQuery) {
  1041. $buttonFooters.each(function () {
  1042. $(this).clone(true, true).appendTo($dialogFooter);
  1043. });
  1044. } else if ($buttonFooters instanceof Array) {
  1045. $.each($buttonFooters, function (index, $button) {
  1046. $button.clone(true, true).appendTo($dialogFooter);
  1047. });
  1048. } else if ($buttonFooters instanceof Object) {
  1049. $.each($buttonFooters, function (key, $button) {
  1050. $button.clone(true, true).appendTo($dialogFooter);
  1051. });
  1052. }
  1053. }
  1054. $dialogContent.click(function (e) {
  1055. e.stopPropagation();
  1056. });
  1057. }
  1058. Dialog.prototype = {
  1059. show: function () {
  1060. if ($mouseGestures.size()) {
  1061. this.$dialog.insertBefore($mouseGestures);
  1062. } else {
  1063. this.$dialog.appendTo(document.body);
  1064. }
  1065. this.isShow = true;
  1066. this.status = 'show';
  1067. },
  1068. close: function () {
  1069. this.$dialog.remove();
  1070. this.isShow = false;
  1071. this.status = 'close';
  1072. }
  1073. };
  1074. return Dialog;
  1075. })();
  1076.  
  1077. var ItemMG = (function () {
  1078. function ItemMG(gesture, action, options) {
  1079. var _this = this;
  1080. this.gesture = gesture;
  1081. this.action = action;
  1082. var $itemMG = $('<div/>', {
  1083. class: 'mglo-item'
  1084. });
  1085. var listIcon = new ListIcon('Gesture', {
  1086. gesture: gesture
  1087. });
  1088. listIcon.$element.appendTo($itemMG);
  1089. var itemActions = [];
  1090. $.each(defaultActions, function (nameGesture, value) {
  1091. if (options.onLink !== true && value.onLink !== true || options.onLink === true && value.onLink === true) {
  1092. var item = {};
  1093. item.value = nameGesture;
  1094. item.label = value.label;
  1095. item.optgroup = value.category;
  1096.  
  1097. $.each(gestures, function (gesture, value) {
  1098. if (value.gesture === nameGesture && nameGesture !== action) {
  1099. item.disabled = true;
  1100. }
  1101. });
  1102.  
  1103. itemActions.push(item);
  1104. }
  1105. });
  1106.  
  1107. var inputMG = new Form.InputMG(undefined, {
  1108. value: gesture
  1109. });
  1110. inputMG.$element.appendTo($itemMG);
  1111.  
  1112. var selectAction = new Form.Select('Action', {
  1113. required: true,
  1114. items: itemActions,
  1115. value: action
  1116. }, function (e) {
  1117. return _this.setAction(e.target.value);
  1118. });
  1119.  
  1120. selectAction.$element.appendTo($itemMG);
  1121. this.listIcon = listIcon;
  1122. this.inputMG = inputMG;
  1123. this.selectAction = selectAction;
  1124. this.$element = $itemMG;
  1125. }
  1126. ItemMG.prototype = {
  1127. setGesture: function (gesture) {
  1128. this.gesture = gesture;
  1129. this.listIcon.setGesture(gesture);
  1130. },
  1131. setAction: function (action) {
  1132. this.action = action;
  1133. this.selectAction.$select.val(action);
  1134. }
  1135. };
  1136. return ItemMG;
  1137. })();
  1138.  
  1139. // var Table = (function () {
  1140. // function Table(options) {
  1141. // var $table = $('<table/>', {
  1142. // class: 'mglo-table'
  1143. // });
  1144. // var $trHeader = $('<tr/>').appendTo($table);
  1145. // options.columns.forEach(function (column) {
  1146. // $('<th/>').text(column.header).appendTo($trHeader);
  1147. // });
  1148.  
  1149. // options.data.forEach(function (row, index) {
  1150. // var $trRow = $('<tr/>').appendTo($table);
  1151. // options.columns.forEach(function (column) {
  1152. // $.each(row, function (key, value) {
  1153. // if (column.binding === key) {
  1154. // var $td = $('<td/>', column).append(value).appendTo($trRow);
  1155. // }
  1156. // });
  1157. // });
  1158. // });
  1159. // this.$element = $table;
  1160. // }
  1161. // return Table;
  1162. // }());
  1163.  
  1164. function showDialogSetting() {
  1165. if (dialogSetting && dialogSetting.isShow) {
  1166. dialogSetting.close();
  1167. }
  1168. dialogSetting = new Dialog('Settings', $contentSetting, $buttonFooterSettings, true);
  1169. dialogSetting.show();
  1170. }
  1171.  
  1172. $contentSetting.append('On Page');
  1173.  
  1174. $.each(gestures, function (gesture, value) {
  1175. if (getGesture(gesture, 'onLink') !== true) {
  1176. var itemMG = new ItemMG(gesture, value.gesture, {});
  1177. itemMG.$element.appendTo($contentSetting);
  1178. }
  1179. });
  1180.  
  1181. $contentSetting.append('On Link');
  1182.  
  1183. $.each(gestures, function (gesture, value) {
  1184. if (getGesture(gesture, 'onLink') === true) {
  1185. var itemMG = new ItemMG(gesture, value.gesture, {
  1186. onLink: true
  1187. });
  1188. itemMG.$element.appendTo($contentSetting);
  1189. }
  1190. });
  1191.  
  1192. $buttonSave.on('click.mglo', function (e) {
  1193. e.preventDefault();
  1194. });
  1195.  
  1196. // GM_registerMenuCommand('Settings', function () {
  1197. // showDialogSetting();
  1198. // });
  1199.  
  1200. $.fn.isScrollToTop = function () {
  1201. return this.scrollTop() === 0;
  1202. };
  1203.  
  1204. $.fn.isScrollToBottom = function () {
  1205. return this.get(0).scrollHeight - this.scrollTop() <= this.outerHeight();
  1206. };
  1207.  
  1208. $.fn.hasVScrollBar = function (includeHidden) {
  1209. var overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
  1210. return !(/(HTML)/.test(this.get(0).tagName)) && overflowRegex.test(this.css('overflow')) && overflowRegex.test(this.css('overflow-y')) && this.get(0).scrollHeight > this.innerHeight();
  1211. };
  1212.  
  1213. $.fn.vScrollBarParent = function (scrollUpOrDown, includeHidden) {
  1214. if (this.hasVScrollBar() && (scrollUpOrDown !== 'up' && scrollUpOrDown !== 'down' || scrollUpOrDown === 'up' && !this.isScrollToTop() || scrollUpOrDown === 'down' && !this.isScrollToBottom())) {
  1215. return this;
  1216. }
  1217. var position = this.css('position'),
  1218. excludeStaticParent = position === 'absolute',
  1219. vScrollBarParent = this.parents().filter(function () {
  1220. var parent = $(this);
  1221. if (excludeStaticParent && parent.css('position') === 'static' || scrollUpOrDown === 'up' && parent.isScrollToTop() || scrollUpOrDown === 'down' && parent.isScrollToBottom()) {
  1222. return false;
  1223. }
  1224. return parent.hasVScrollBar();
  1225. }).eq(0);
  1226. return position === 'fixed' || !vScrollBarParent.length ? $('html, body') : vScrollBarParent;
  1227. };
  1228. })();