Hordes UI Mod

Various UI mods for Hordes.io.

当前为 2019-12-21 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Hordes UI Mod
  3. // @version 0.1
  4. // @description Various UI mods for Hordes.io.
  5. // @author Sakaiyo
  6. // @match https://hordes.io/play
  7. // @grant GM_addStyle
  8. // @namespace https://greasyfork.org/users/160017
  9. // ==/UserScript==
  10. /**
  11. * TODO: Implement GM and lvlup chat tabs
  12. * TODO: Implement saving of dragged UI location
  13. * TODO: (Maybe) Implement saving of chat filters
  14. * TODO: Implement saving of map and chat size
  15. * TODO: Implement chat tabs
  16. * TODO: Implement inventory sorting
  17. * TODO: (Maybe) Improved healer party frames
  18. * TODO: Can we speed up windows becoming draggable? Slight delay
  19. */
  20. (function() {
  21. 'use strict';
  22.  
  23. const state = {};
  24. const CHAT_LVLUP_CLASS = 'js-chat-lvlup';
  25. const CHAT_GM_CLASS = 'js-chat-gm';
  26.  
  27. // UPDATING STYLES BELOW - Must be invoked in main function
  28. GM_addStyle(`
  29. /* Transparent chat bg color */
  30. .frame.svelte-1vrlsr3 {
  31. background: rgba(0,0,0,0.4);
  32. }
  33.  
  34. /* Allows windows to be moved */
  35. .window {
  36. position: relative;
  37. }
  38.  
  39. /* Enable chat & map resize */
  40. .js-chat-resize {
  41. resize: both;
  42. overflow: auto;
  43. }
  44. .js-map-resize:hover {
  45. resize: both;
  46. overflow: auto;
  47. direction: rtl;
  48. }
  49.  
  50. /* The browser resize icon */
  51. *::-webkit-resizer {
  52. background: linear-gradient(to right, rgba(51, 77, 80, 0), rgba(203, 202, 165, 0.5));
  53. border-radius: 8px;
  54. box-shadow: 0 1px 1px rgba(0,0,0,1);
  55. }
  56. *::-moz-resizer {
  57. background: linear-gradient(to right, rgba(51, 77, 80, 0), rgba(203, 202, 165, 0.5));
  58. border-radius: 8px;
  59. box-shadow: 0 1px 1px rgba(0,0,0,1);
  60. }
  61. `);
  62.  
  63.  
  64. // MAIN MODS BELOW
  65. const setupDom = {
  66. // Commenting this out for now, the added UI doesn't do anything yet
  67. // newChatFilters: () => {
  68. // const $channelselect = document.querySelector('.channelselect');
  69. // if (!document.querySelector(`.${CHAT_LVLUP_CLASS}`)) {
  70. // const $lvlup = createElement({
  71. // element: 'small',
  72. // class: `btn border black textgrey ${CHAT_LVLUP_CLASS}`,
  73. // content: 'lvlup'
  74. // });
  75. // $channelselect.appendChild($lvlup);
  76. // }
  77. // if (!document.querySelector(`.${CHAT_GM_CLASS}`)) {
  78. // const $gm = createElement({
  79. // element: 'small',
  80. // class: `btn border black textgrey ${CHAT_GM_CLASS}`,
  81. // content: 'GM'
  82. // });
  83. // $channelselect.appendChild($gm);
  84. // }
  85. // },
  86. };
  87.  
  88. const wireDom = {
  89. newChatFilters: () => {
  90.  
  91. },
  92.  
  93. // Drag all windows by their header
  94. draggableUIWindows: () => {
  95. Array.from(document.querySelectorAll('.window:not(.js-can-move)')).forEach($window => {
  96. dragElement($window, $window.querySelector('.titleframe'));
  97. $window.classList.add('js-can-move');
  98. });
  99. },
  100.  
  101. // Resize chat and map
  102. resizableUi: () => {
  103. document.querySelector('#chat').parentNode.classList.add('js-chat-resize');
  104.  
  105. const $map = document.querySelector('.svelte-hiyby7');
  106. $map.classList.add('js-map-resize');
  107.  
  108. // On resize of map, resize canvas to match
  109. const resizeObserver = new ResizeObserver(() => {
  110. // Get real values of map height/width, excluding padding/margin/etc
  111. let mapWidth = window.getComputedStyle($map, null).getPropertyValue('width');
  112. let mapHeight = window.getComputedStyle($map, null).getPropertyValue('height');
  113. mapWidth = Number(mapWidth.slice(0, -2));
  114. mapHeight = Number(mapHeight.slice(0, -2));
  115.  
  116. // If height/width are 0 or unset, don't resize canvas
  117. if (!mapWidth || !mapHeight) {
  118. return;
  119. }
  120.  
  121. const $canvas = $map.querySelector('canvas');
  122. if ($canvas.width !== mapWidth) {
  123. $canvas.width = mapWidth;
  124. }
  125.  
  126. if ($canvas.height !== mapHeight) {
  127. $canvas.height = mapHeight;
  128. }
  129. })
  130. resizeObserver.observe($map);
  131. },
  132. };
  133.  
  134. // Add new DOM, wire it up, then continuously rerun specific methods whenever UI changes
  135. function initialize() {
  136. Object.keys(setupDom).forEach((domMethod) => setupDom[domMethod]());
  137. Object.keys(wireDom).forEach((domMethod) => wireDom[domMethod]());
  138.  
  139. // Continuously re-run specific wireDom methods that need to be executed on UI change
  140. const rerunOnChange = () => {
  141. // If new window appears, e.g. even if window is closed and reopened, we need to rewire it
  142. wireDom.draggableUIWindows();
  143. };
  144. document.querySelector('.layout').addEventListener('click', rerunOnChange);
  145. document.querySelector('.layout').addEventListener('keypress', rerunOnChange);
  146. }
  147.  
  148. // Initialize mods once UI DOM has loaded
  149. const pageObserver = new MutationObserver((_, observer) => {
  150. const isUiLoaded = !!document.querySelector('.layout');
  151. if (isUiLoaded) {
  152. initialize();
  153. }
  154. });
  155. pageObserver.observe(document.body, { attributes: true, childList: true })
  156.  
  157. // UTIL METHODS
  158. // Nicer impl to create elements in one method call
  159. function createElement(args) {
  160. const $node = document.createElement(args.element);
  161. if (args.class) { $node.className = args.class; }
  162. if (args.content) { $node.innerHTML = args.content; }
  163. if (args.src) { $node.src = args.src; }
  164. return $node;
  165. }
  166.  
  167. // ...Can't remember why I added this.
  168. // TODO: Remove this if not using. Can access chat input with it
  169. function simulateEnterPress() {
  170. const kbEvent = new KeyboardEvent("keydown", {
  171. bubbles: true, cancelable: true, keyCode: 13
  172. });
  173. document.body.dispatchEvent(kbEvent);
  174. }
  175.  
  176. // Credit: https://stackoverflow.com/a/14234618 (Has been slightly modified)
  177. // $draggedElement is the item that will be dragged.
  178. // $dragTrigger is the element that must be held down to drag $draggedElement
  179. function dragElement($draggedElement, $dragTrigger) {
  180. let offset = [0,0];
  181. let isDown = false;
  182. $dragTrigger.addEventListener('mousedown', function(e) {
  183. isDown = true;
  184. offset = [
  185. $draggedElement.offsetLeft - e.clientX,
  186. $draggedElement.offsetTop - e.clientY
  187. ];
  188. }, true);
  189. document.addEventListener('mouseup', function() {
  190. isDown = false;
  191. }, true);
  192.  
  193. document.addEventListener('mousemove', function(e) {
  194. event.preventDefault();
  195. if (isDown) {
  196. $draggedElement.style.left = (e.clientX + offset[0]) + 'px';
  197. $draggedElement.style.top = (e.clientY + offset[1]) + 'px';
  198. }
  199. }, true);
  200. }
  201. })();