您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various UI mods for Hordes.io.
当前为
// ==UserScript== // @name Hordes UI Mod // @version 0.1 // @description Various UI mods for Hordes.io. // @author Sakaiyo // @match https://hordes.io/play // @grant GM_addStyle // @namespace https://greasyfork.org/users/160017 // ==/UserScript== /** * TODO: Implement GM and lvlup chat tabs * TODO: Implement saving of dragged UI location * TODO: (Maybe) Implement saving of chat filters * TODO: Implement saving of map and chat size * TODO: Implement chat tabs * TODO: Implement inventory sorting * TODO: (Maybe) Improved healer party frames * TODO: Can we speed up windows becoming draggable? Slight delay */ (function() { 'use strict'; const state = {}; const CHAT_LVLUP_CLASS = 'js-chat-lvlup'; const CHAT_GM_CLASS = 'js-chat-gm'; // UPDATING STYLES BELOW - Must be invoked in main function GM_addStyle(` /* Transparent chat bg color */ .frame.svelte-1vrlsr3 { background: rgba(0,0,0,0.4); } /* Allows windows to be moved */ .window { position: relative; } /* Enable chat & map resize */ .js-chat-resize { resize: both; overflow: auto; } .js-map-resize:hover { resize: both; overflow: auto; direction: rtl; } /* The browser resize icon */ *::-webkit-resizer { background: linear-gradient(to right, rgba(51, 77, 80, 0), rgba(203, 202, 165, 0.5)); border-radius: 8px; box-shadow: 0 1px 1px rgba(0,0,0,1); } *::-moz-resizer { background: linear-gradient(to right, rgba(51, 77, 80, 0), rgba(203, 202, 165, 0.5)); border-radius: 8px; box-shadow: 0 1px 1px rgba(0,0,0,1); } `); // MAIN MODS BELOW const setupDom = { // Commenting this out for now, the added UI doesn't do anything yet // newChatFilters: () => { // const $channelselect = document.querySelector('.channelselect'); // if (!document.querySelector(`.${CHAT_LVLUP_CLASS}`)) { // const $lvlup = createElement({ // element: 'small', // class: `btn border black textgrey ${CHAT_LVLUP_CLASS}`, // content: 'lvlup' // }); // $channelselect.appendChild($lvlup); // } // if (!document.querySelector(`.${CHAT_GM_CLASS}`)) { // const $gm = createElement({ // element: 'small', // class: `btn border black textgrey ${CHAT_GM_CLASS}`, // content: 'GM' // }); // $channelselect.appendChild($gm); // } // }, }; const wireDom = { newChatFilters: () => { }, // Drag all windows by their header draggableUIWindows: () => { Array.from(document.querySelectorAll('.window:not(.js-can-move)')).forEach($window => { dragElement($window, $window.querySelector('.titleframe')); $window.classList.add('js-can-move'); }); }, // Resize chat and map resizableUi: () => { document.querySelector('#chat').parentNode.classList.add('js-chat-resize'); const $map = document.querySelector('.svelte-hiyby7'); $map.classList.add('js-map-resize'); // On resize of map, resize canvas to match const resizeObserver = new ResizeObserver(() => { // Get real values of map height/width, excluding padding/margin/etc let mapWidth = window.getComputedStyle($map, null).getPropertyValue('width'); let mapHeight = window.getComputedStyle($map, null).getPropertyValue('height'); mapWidth = Number(mapWidth.slice(0, -2)); mapHeight = Number(mapHeight.slice(0, -2)); // If height/width are 0 or unset, don't resize canvas if (!mapWidth || !mapHeight) { return; } const $canvas = $map.querySelector('canvas'); if ($canvas.width !== mapWidth) { $canvas.width = mapWidth; } if ($canvas.height !== mapHeight) { $canvas.height = mapHeight; } }) resizeObserver.observe($map); }, }; // Add new DOM, wire it up, then continuously rerun specific methods whenever UI changes function initialize() { Object.keys(setupDom).forEach((domMethod) => setupDom[domMethod]()); Object.keys(wireDom).forEach((domMethod) => wireDom[domMethod]()); // Continuously re-run specific wireDom methods that need to be executed on UI change const rerunOnChange = () => { // If new window appears, e.g. even if window is closed and reopened, we need to rewire it wireDom.draggableUIWindows(); }; document.querySelector('.layout').addEventListener('click', rerunOnChange); document.querySelector('.layout').addEventListener('keypress', rerunOnChange); } // Initialize mods once UI DOM has loaded const pageObserver = new MutationObserver((_, observer) => { const isUiLoaded = !!document.querySelector('.layout'); if (isUiLoaded) { initialize(); } }); pageObserver.observe(document.body, { attributes: true, childList: true }) // UTIL METHODS // Nicer impl to create elements in one method call function createElement(args) { const $node = document.createElement(args.element); if (args.class) { $node.className = args.class; } if (args.content) { $node.innerHTML = args.content; } if (args.src) { $node.src = args.src; } return $node; } // ...Can't remember why I added this. // TODO: Remove this if not using. Can access chat input with it function simulateEnterPress() { const kbEvent = new KeyboardEvent("keydown", { bubbles: true, cancelable: true, keyCode: 13 }); document.body.dispatchEvent(kbEvent); } // Credit: https://stackoverflow.com/a/14234618 (Has been slightly modified) // $draggedElement is the item that will be dragged. // $dragTrigger is the element that must be held down to drag $draggedElement function dragElement($draggedElement, $dragTrigger) { let offset = [0,0]; let isDown = false; $dragTrigger.addEventListener('mousedown', function(e) { isDown = true; offset = [ $draggedElement.offsetLeft - e.clientX, $draggedElement.offsetTop - e.clientY ]; }, true); document.addEventListener('mouseup', function() { isDown = false; }, true); document.addEventListener('mousemove', function(e) { event.preventDefault(); if (isDown) { $draggedElement.style.left = (e.clientX + offset[0]) + 'px'; $draggedElement.style.top = (e.clientY + offset[1]) + 'px'; } }, true); } })();