Greasy Fork is available in English.

OSU navigator

Use key "z" and "x" as mouse left and right, and displays your mouse cursor as osu yellow mouse cursor

  1. /* eslint-disable no-multi-spaces */
  2.  
  3. // ==UserScript==
  4. // @name OSU navigator
  5. // @name:zh-CN 鼠标OSU化
  6. // @name:zh-TW 滑鼠OSU化
  7. // @name:ko 마우스 OSU화
  8. // @namespace OSU_NAVIGATOR
  9. // @version 0.2
  10. // @description Use key "z" and "x" as mouse left and right, and displays your mouse cursor as osu yellow mouse cursor
  11. // @description:zh-CN 使用"z"和"x"键作为鼠标左右键,并将鼠标样式显示为圆形亮黄osu光标
  12. // @description:zh-TW 使用“z”和“x”鍵作為滑鼠左右鍵,並將滑鼠樣式顯示為圓形亮黃osu光標
  13. // @description:ko "z" 및 "x" 키를 마우스 좌우 키로 사용하고 마우스 스타일을 둥근 밝은 노란색 osu 커서로 표시합니다
  14. // @author PY-DNG
  15. // @license MIT
  16. // @match http*://*/*
  17. // @icon https://api.iowen.cn/favicon/get.php?url=osu.ppy.sh
  18. // @grant none
  19. // ==/UserScript==
  20.  
  21. (function() {
  22. 'use strict';
  23.  
  24. // Arguments: level=LogLevel.Info, logContent, asObject=false
  25. // Needs one call "DoLog();" to get it initialized before using it!
  26. function DoLog() {
  27. // Global log levels set
  28. window.LogLevel = {
  29. None: 0,
  30. Error: 1,
  31. Success: 2,
  32. Warning: 3,
  33. Info: 4,
  34. }
  35. window.LogLevelMap = {};
  36. window.LogLevelMap[LogLevel.None] = {prefix: '' , color: 'color:#ffffff'}
  37. window.LogLevelMap[LogLevel.Error] = {prefix: '[Error]' , color: 'color:#ff0000'}
  38. window.LogLevelMap[LogLevel.Success] = {prefix: '[Success]' , color: 'color:#00aa00'}
  39. window.LogLevelMap[LogLevel.Warning] = {prefix: '[Warning]' , color: 'color:#ffa500'}
  40. window.LogLevelMap[LogLevel.Info] = {prefix: '[Info]' , color: 'color:#888888'}
  41. window.LogLevelMap[LogLevel.Elements] = {prefix: '[Elements]', color: 'color:#000000'}
  42.  
  43. // Current log level
  44. DoLog.logLevel = LogLevel.Info; // Info Warning Success Error
  45.  
  46. // Log counter
  47. DoLog.logCount === undefined && (DoLog.logCount = 0);
  48. if (++DoLog.logCount > 512) {
  49. console.clear();
  50. DoLog.logCount = 0;
  51. }
  52.  
  53. // Get args
  54. let level, logContent, asObject;
  55. switch (arguments.length) {
  56. case 1:
  57. level = LogLevel.Info;
  58. logContent = arguments[0];
  59. asObject = false;
  60. break;
  61. case 2:
  62. level = arguments[0];
  63. logContent = arguments[1];
  64. asObject = false;
  65. break;
  66. case 3:
  67. level = arguments[0];
  68. logContent = arguments[1];
  69. asObject = arguments[2];
  70. break;
  71. default:
  72. level = LogLevel.Info;
  73. logContent = 'DoLog initialized.';
  74. asObject = false;
  75. break;
  76. }
  77.  
  78. // Log when log level permits
  79. if (level <= DoLog.logLevel) {
  80. let msg = '%c' + LogLevelMap[level].prefix;
  81. let subst = LogLevelMap[level].color;
  82.  
  83. if (asObject) {
  84. msg += ' %o';
  85. } else {
  86. switch(typeof(logContent)) {
  87. case 'string': msg += ' %s'; break;
  88. case 'number': msg += ' %d'; break;
  89. case 'object': msg += ' %o'; break;
  90. }
  91. }
  92.  
  93. console.log(msg, subst, logContent);
  94. }
  95. }
  96. DoLog();
  97.  
  98. main();
  99. function main() {
  100. // Terminal element event listeners
  101. /*
  102. for (const elm of document.querySelectorAll('*')) {
  103. dealElement(elm);
  104. }
  105. document.addEventListener('DOMNodeInserted', (e) => {if(!e.target){debugger;}dealElement(e.target);});
  106. */
  107. document.addEventListener('mousemove', function(e) {
  108. const elm = document.elementFromPoint(e.x, e.y);
  109. removeListeners(window.OSUMouse.target);
  110. addListeners(elm);
  111. window.OSUMouse.target = elm;
  112. }, {
  113. capture: true,
  114. passive: true
  115. })
  116.  
  117. // Global event listeners
  118. document.body.onkeydown = keyDownListener;
  119. document.body.onkeyup = keyUpListener;
  120.  
  121. // Global status recorder
  122. window.OSUMouse = {
  123. ctrlKey: false,
  124. altKey: false,
  125. shiftKey: false,
  126. metaKey: false,
  127. target: document.body
  128. };
  129.  
  130. // Change cursor
  131. osuMouseCursor();
  132. }
  133.  
  134. function addListeners(elm) {
  135. elm.addEventListener('mousemove', recordMouseStatus);
  136. }
  137.  
  138. function removeListeners(elm) {
  139. // Record mouse status
  140. elm.removeEventListener('mousemove', recordMouseStatus);
  141. }
  142.  
  143. function recordMouseStatus(e) {
  144. const props = ['screenX', 'screenY', 'clientX', 'clientY', 'relatedTarget', 'region']
  145. for (const prop of props) {
  146. window.OSUMouse[prop] = e[prop];
  147. }
  148. }
  149.  
  150. function keyDownListener(e) {
  151. switch (e.key) {
  152. case 'Control':
  153. window.OSUMouse.ctrlKey = true;
  154. //DoLog(window.OSUMouse);
  155. break;
  156. case 'Shift':
  157. window.OSUMouse.shiftKey = true;
  158. //DoLog(window.OSUMouse);
  159. break;
  160. case 'Alt':
  161. window.OSUMouse.altKey = true;
  162. //DoLog(window.OSUMouse);
  163. break;
  164. case 'Meta':
  165. window.OSUMouse.metaKey = true;
  166. //DoLog(window.OSUMouse);
  167. break;
  168. case 'z':
  169. case 'Z':
  170. case 'x':
  171. case 'X':
  172. dispatchMouseDown(e.target);
  173. break;
  174. }
  175. }
  176.  
  177. function keyUpListener(e) {
  178. switch (e.key) {
  179. case 'Control':
  180. window.OSUMouse.ctrlKey = false;
  181. //DoLog(window.OSUMouse);
  182. break;
  183. case 'Shift':
  184. window.OSUMouse.shiftKey = false;
  185. //DoLog(window.OSUMouse);
  186. break;
  187. case 'Alt':
  188. window.OSUMouse.altKey = false;
  189. //DoLog(window.OSUMouse);
  190. break;
  191. case 'Meta':
  192. window.OSUMouse.metaKey = false;
  193. //DoLog(window.OSUMouse);
  194. break;
  195. case 'z':
  196. case 'Z':
  197. !inputing() && dispatchMouseLeftUp();
  198. break;
  199. case 'x':
  200. case 'X':
  201. !inputing() && dispatchMouseRightUp();
  202. break;
  203. }
  204. }
  205.  
  206. function dispatchMouseDown() {
  207. const mouseEventInit = {};
  208. for (const [key, value] of Object.entries(window.OSUMouse)) {
  209. mouseEventInit[key] = value;
  210. }
  211. mouseEventInit.bubbles = true;
  212. const focusEventInit = {relatedTarget: window.OSUMouse.relatedTarget, bubbles: true};
  213. const mouseLeft = new MouseEvent('mousedown', mouseEventInit);
  214. const focus = new FocusEvent('focus', focusEventInit);
  215. window.OSUMouse.target.dispatchEvent(focus);
  216. window.OSUMouse.target.dispatchEvent(mouseLeft);
  217. }
  218.  
  219. function dispatchMouseLeftUp() {
  220. const mouseEventInit = {};
  221. for (const [key, value] of Object.entries(window.OSUMouse)) {
  222. mouseEventInit[key] = value;
  223. }
  224. mouseEventInit.bubbles = true;
  225. const mouseRight = new MouseEvent('mouseup', mouseEventInit);
  226. const mouseclick = new MouseEvent('click', mouseEventInit);
  227. window.OSUMouse.target.dispatchEvent(mouseRight);
  228. window.OSUMouse.target.dispatchEvent(mouseclick);
  229. }
  230.  
  231. function dispatchMouseRightUp() {
  232. const mouseEventInit = {};
  233. for (const [key, value] of Object.entries(window.OSUMouse)) {
  234. mouseEventInit[key] = value;
  235. }
  236. mouseEventInit.bubbles = true;
  237. const mousecontextmenu = new MouseEvent('contentmenu', mouseEventInit);
  238. window.OSUMouse.target.dispatchEvent(mousecontextmenu);
  239. }
  240.  
  241. function inputing() {
  242. return document.activeElement && [HTMLInputElement, HTMLTextAreaElement].some((o) => (document.activeElement instanceof o));
  243. }
  244.  
  245. function osuMouseCursor() {
  246. // Cursor
  247. const OSUCursor = 'data:image/x-icon;base64,AAACAAEAICAAAA8ADwCoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALvXAQDW9gEA1vYBANb2AQDW9gEAu9cBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW9gEA1vYDANb2BADW9gYA1vYIANb2CgDW9gsA1vYLANb2CgDW9ggA1vYGANb2BADW9gIA1vYBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW9gEA1vYDANb2BwDW9gsA1vYQANb2FQDW9hoA1vYdANb2HwDW9h8A1vYdANb2GQDW9hUA1vYPANb2CwDW9gYA1vYDANb2AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1vYCANb2BgDW9gsA1vYSANb2GwDW9iQA1vYtANb2NADW9joA1vY9ANb2PQDW9joA1vY0ANb2LQDW9iQA1vYbANb2EgDW9gsA1vYGANb2AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANb2AwDW9ggA1vYPANb2GgDW9icA1vY0ANb2QwDW9lAA1vZaANb2YQDW9mQA1vZkANb2YQDW9loA1vZPANb2QgDW9jQA1vYnANb2GgDW9g8A1vYHANb2AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW9gMA1vYIANb2EgDW9h8A1vYvANb2QwDW9lYA1vZqANb2egDW9oYA1vaOANb2kwDW9pMA1vaOANb2hgDW9noA1vZqANb2VgDW9kIA1vYvANb2HgDW9hEA1vYIANb2AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1vYCANb2CADW9hIA1vYhANb2NQDW9k4A1vZmANb2fgDW9pMA1vakANb2sADW9rcA1va7ANb2uwDW9rcA1vawANb2pADW9pMA1vZ+ANb2ZQDW9k0A1vY1ANb2IQDW9hEA1vYHANb2AgAAAAAAAAAAAAAAAAAAAAAAAAAAANb2AQDW9gYA1vYPANb2HwDW9jUA1vZQANb2bgDW9osA1vakANb2uQDW9sgA1vbSANb22ADW9tsA1vbbANb22ADW9tIA1vbIANb2uQDW9qQA1vaLANb2bQDW9lAA1vY1ANb2HgDW9g8A1vYGANb2AQAAAAAAAAAAAAAAAAAAAAAA1vYDANb2CwDW9hoA1vYvANb2TgDW9m4A1vaOANb2rADW9sUA1vbWANb24wDW9uoA1vbuANb28ADW9vAA1vbvANb26gDW9uMA1vbXANb2xQDW9qwA1vaOANb2bQDW9k0A1vYvANb2GgDW9gsA1vYDAAAAAAAAAAAAAAAAANb2AQDW9gcA1vYSANb2JwDW9kMA1vZmANb2iwDW9qwA1vbIANb23gDW9usA1vb0ANL2+Tza+PtQ4vj8UOL4/Dza+PsA0vb5ANb29ADW9uwA1vbeANb2yQDW9qwA1vaLANb2ZQDW9kIA1vYnANb2EgDW9gYA1vYBAAAAAAAAAAAA1vYDANb2CwDW9hsA1vY0ANb2VgDW9n4A1vakANb2xQDW9t4A1vbuANb290XQ/P3m+/7+/P///////////////P///+b7/v5F0Pz9ANb29wDW9u4A1vbeANb2xQDW9qQA1vZ+ANb2VgDW9jQA1vYbANb2CwDW9gIAAAAAAAAAAADW9gQA1vYQANb2JADW9kMA1vZqANb2kwDW9rkA1vbWANb26wDW9vdx5fz9//////////////////////////////////////////9x5fz9ANb29wDW9uwA1vbXANb2uQDW9pMA1vZqANb2QgDW9iQA1vYPANb2BAAAAAAAAAAAANb2BgDW9hUA1vYtANb2UADW9noA1vakANb2yADW9uMA1vb0RdD8/f////////////////////////////////////////////////////9F0Pz9ANb29ADW9uMA1vbIANb2pADW9noA1vZPANb2LQDW9hUA1vYGAAAAAAC71wEA1vYIANb2GgDW9jQA1vZaANb2hgDW9rAA1vbSANb26gDS9vnm+/7+/////////////////////////////////////////////////////+b7/v4A0vb5ANb26gDW9tIA1vawANb2hgDW9loA1vY0ANb2GQDW9ggAu9cBANb2AQDW9goA1vYdANb2OgDW9mEA1vaOANb2twDW9tgA1vbuPNr4+/z//////////////////////////////////////////////////////////P///zza+PsA1vbvANb22ADW9rcA1vaOANb2YQDW9joA1vYdANb2CgDW9gEA1vYBANb2CwDW9h8A1vY9ANb2ZADW9pMA1va7ANb22wDW9vBQ4vj8////////////////////////////////////////////////////////////////UOL4/ADW9vAA1vbbANb2uwDW9pMA1vZkANb2PQDW9h8A1vYLANb2AQDW9gEA1vYLANb2HwDW9j0A1vZkANb2kwDW9rsA1vbbANb28FDi+Pz///////////////////////////////////////////////////////////////9Q4vj8ANb28ADW9tsA1va7ANb2kwDW9mQA1vY9ANb2HwDW9gsA1vYBANb2AQDW9goA1vYdANb2OgDW9mEA1vaOANb2twDW9tgA1vbvPNr4+/z//////////////////////////////////////////////////////////P///zza+PsA1vbuANb22ADW9rcA1vaOANb2YQDW9joA1vYdANb2CgDW9gEAu9cBANb2CADW9hkA1vY0ANb2WgDW9oYA1vawANb20gDW9uoA0vb55vv+/v/////////////////////////////////////////////////////m+/7+ANL2+QDW9uoA1vbSANb2sADW9oYA1vZaANb2NADW9hoA1vYIALvXAQAAAAAA1vYGANb2FQDW9i0A1vZPANb2egDW9qQA1vbIANb24wDW9vRF0Pz9/////////////////////////////////////////////////////0XQ/P0A1vb0ANb24wDW9sgA1vakANb2egDW9lAA1vYtANb2FQDW9gYAAAAAAAAAAADW9gQA1vYPANb2JADW9kIA1vZqANb2kwDW9rkA1vbXANb27ADW9vdx5fz9////////////////////////////////////////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';
  248. const CSSCursor = 'body {cursor: url("{C}"), auto !important;}'.replace('{C}', OSUCursor);
  249. addStyle(CSSCursor, 'osu_cursor');
  250.  
  251. /*
  252. // Canvas
  253. const canvas = document.createElement('canvas');
  254. const CSSCanvas = '#osu_cursor_canvas {position: fixed; pointer-events: none; z-index: 99999999}';
  255. const img = new Image();
  256. img.onload = function() {
  257. const ctx = canvas.getContext('2d');
  258. const half = img.width / 2;
  259. canvas.width = img.width;
  260. canvas.height = img.height;
  261. ctx.drawImage(img, 0, 0);
  262. canvas.id = 'osu_cursor_canvas';
  263. document.body.addEventListener('mousemove', (e) => {
  264. canvas.style.top = (e.clientY - half).toString() + 'px';
  265. canvas.style.left = (e.clientX - half).toString() + 'px';
  266. });
  267. document.body.appendChild(canvas);
  268. };
  269. img.src = OSUCursor;
  270. addStyle(CSSCanvas);
  271. */
  272. }
  273.  
  274. // Just stopPropagation and preventDefault
  275. function destroyEvent(e) {
  276. if (!e) {return false;};
  277. if (!e instanceof Event) {return false;};
  278. e.stopPropagation();
  279. e.preventDefault();
  280. }
  281.  
  282. // Append a style text to document(<head>) with a <style> element
  283. function addStyle(css, id) {
  284. const style = document.createElement("style");
  285. id && (style.id = id);
  286. style.textContent = css;
  287. for (const elm of document.querySelectorAll('#'+id)) {
  288. elm.parentElement && elm.parentElement.removeChild(elm);
  289. }
  290. document.head.appendChild(style);
  291. }
  292. })();