Changed userscript functionality

We now are checking for the GM_info object used by userscripts, and using location.href to run the script in the page context if it's found.
This commit is contained in:
FlamedDogo99 2024-06-28 09:50:06 -06:00
parent cdec31ab74
commit ef03fcb4b2

View File

@ -6,58 +6,65 @@
// @downloadURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js // @downloadURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
// @license Apache License 2.0 - http://www.apache.org/licenses/ // @license Apache License 2.0 - http://www.apache.org/licenses/
// @match https://eaglercraft.com/mc/* // @match https://eaglercraft.com/mc/*
// @version 3.0.5b // @version 3.0.6
// @updateURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js // @updateURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
// @run-at document-start // @run-at document-start
// @grant none // @grant none
// ==/UserScript== // ==/UserScript==
// Removed brainless unsafeWindow console.log("Eagler Mobile v3.0.6");
console.log("Eagler Mobile v3.0.5b") // Check for object defined by most userscript applications, and run the script in the page context if so
// TODO: remove the mobile check is implement the dynamic enabling and disabling of individual features
function isMobile() { if (typeof GM_info !== "undefined") {
location.href = "javascript:(" + encodeURI(main) + ")();";
} else {
main();
}
// TODO: remove the mobile check and implement the dynamic enabling and disabling of individual features
function main() {
function isMobile() {
try { try {
document.createEvent("TouchEvent"); document.createEvent("TouchEvent");
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
} }
} }
if(!isMobile()) { if(!isMobile()) {
alert("WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!"); alert("WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!");
} }
// TODO: consolidate all of these into a single object? // TODO: consolidate all of these into a single object?
window.crouchLock = false; // Used for crouch mobile control window.crouchLock = false; // Used for crouch mobile control
window.sprintLock = false; // Used for sprint mobile control window.sprintLock = false; // Used for sprint mobile control
window.keyboardFix = false; // keyboardFix ? "Standard Keyboard" : "Compatibility Mode" window.keyboardFix = false; // keyboardFix ? "Standard Keyboard" : "Compatibility Mode"
window.inputFix = false; // If true, Duplicate Mode window.inputFix = false; // If true, Duplicate Mode
window.blockNextInput = false; // Used for Duplicate Mode window.blockNextInput = false; // Used for Duplicate Mode
window.hiddenInputFocused = false; // Used for keyboard display on mobile window.hiddenInputFocused = false; // Used for keyboard display on mobile
window.canvasTouchMode = 0; // Used for canvas touch handling window.canvasTouchMode = 0; // Used for canvas touch handling
/* /*
0 Idle 0 Idle
1 Touch initiated 1 Touch initiated
2 Primary touch 2 Primary touch
3 Secondary touch 3 Secondary touch
4 Scroll 4 Scroll
5 Finished 5 Finished
*/ */
window.canvasTouchStartX = null; window.canvasTouchStartX = null;
window.canvasTouchStartY = null; window.canvasTouchStartY = null;
window.canvasTouchPreviousX = null; window.canvasTouchPreviousX = null;
window.canvasTouchPreviousY = null; window.canvasTouchPreviousY = null;
window.canvasPrimaryID = null; window.canvasPrimaryID = null;
window.buttonTouchStartX = null; window.buttonTouchStartX = null;
// charCodeAt is designed for unicode characters, and doesn't match the behavior of the keyCodes used by KeyboardEvents, thus necessitating this function // charCodeAt is designed for unicode characters, and doesn't match the behavior of the keyCodes used by KeyboardEvents, thus necessitating this function
String.prototype.toKeyCode = function() { String.prototype.toKeyCode = function() {
const keyCodeList = {"0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "backspace": 8, "tab": 9, "enter": 13, "shift": 16, "ctrl": 17, "alt": 18, "pause_break": 19, "caps_lock": 20, "escape": 27, " ": 32, "page_up": 33, "page_down": 34, "end": 35, "home": 36, "left_arrow": 37, "up_arrow": 38, "right_arrow": 39, "down_arrow": 40, "insert": 45, "delete": 46, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, "left_window_key": 91, "right_window_key": 92, "select_key": 93, "numpad_0": 96, "numpad_1": 97, "numpad_2": 98, "numpad_3": 99, "numpad_4": 100, "numpad_5": 101, "numpad_6": 102, "numpad_7": 103, "numpad_8": 104, "numpad_9": 105, "*": 106, "+": 107, "-": 109, ".": 110, "/": 111, "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, "num_lock": 144, "scroll_lock": 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "\u0060": 192, "[": 219, "\u005C": 220, "]": 221, "\u0022": 222}; const keyCodeList = {"0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "backspace": 8, "tab": 9, "enter": 13, "shift": 16, "ctrl": 17, "alt": 18, "pause_break": 19, "caps_lock": 20, "escape": 27, " ": 32, "page_up": 33, "page_down": 34, "end": 35, "home": 36, "left_arrow": 37, "up_arrow": 38, "right_arrow": 39, "down_arrow": 40, "insert": 45, "delete": 46, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, "left_window_key": 91, "right_window_key": 92, "select_key": 93, "numpad_0": 96, "numpad_1": 97, "numpad_2": 98, "numpad_3": 99, "numpad_4": 100, "numpad_5": 101, "numpad_6": 102, "numpad_7": 103, "numpad_8": 104, "numpad_9": 105, "*": 106, "+": 107, "-": 109, ".": 110, "/": 111, "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, "num_lock": 144, "scroll_lock": 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "\u0060": 192, "[": 219, "\u005C": 220, "]": 221, "\u0022": 222};
return keyCodeList[this]; return keyCodeList[this];
} }
// Overrides the addEventListener behavior to all code injection on keydown event listeners. This function has thrown TypeErrors on some Android devices because fn is not recognized as a function // Overrides the addEventListener behavior to all code injection on keydown event listeners. This function has thrown TypeErrors on some Android devices because fn is not recognized as a function
// This is used by Compatibility Mode to block invalid keyEvents // This is used by Compatibility Mode to block invalid keyEvents
const _addEventListener = EventTarget.prototype.addEventListener; const _addEventListener = EventTarget.prototype.addEventListener;
Object.defineProperty(EventTarget.prototype, "addEventListener", { Object.defineProperty(EventTarget.prototype, "addEventListener", {
value: function (type, fn, ...rest) { value: function (type, fn, ...rest) {
if(type == 'keydown') { // Check if a keydown event is being added if(type == 'keydown') { // Check if a keydown event is being added
_addEventListener.call(this, type, function(...args) { _addEventListener.call(this, type, function(...args) {
@ -70,21 +77,21 @@ Object.defineProperty(EventTarget.prototype, "addEventListener", {
_addEventListener.call(this, type, fn, ...rest); _addEventListener.call(this, type, fn, ...rest);
} }
} }
}); });
// Overrides preventDefault, because on some (Android) devices you couldn't type into hiddenInput // Overrides preventDefault, because on some (Android) devices you couldn't type into hiddenInput
const _preventDefault = Event.prototype.preventDefault; const _preventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function(shouldBypass) { Event.prototype.preventDefault = function(shouldBypass) {
if(document.activeElement.id != "hiddenInput" || shouldBypass) { // activeElement is what element is currently focused if(document.activeElement.id != "hiddenInput" || shouldBypass) { // activeElement is what element is currently focused
this._preventDefault = _preventDefault; this._preventDefault = _preventDefault;
this._preventDefault(); this._preventDefault();
} }
} }
// Key and mouse events // Key and mouse events
// Note: the client must have the key, keyCode, and which parameters defined or it will crash // Note: the client must have the key, keyCode, and which parameters defined or it will crash
// Note: for text inputs, the client only reads from the "key" paramater // Note: for text inputs, the client only reads from the "key" paramater
// * an exception to this appears to be the shift and backspace key // * an exception to this appears to be the shift and backspace key
// Note: for inGame inputs, the client only reads from the "keyCode character" // Note: for inGame inputs, the client only reads from the "keyCode character"
function keyEvent(name, state) { function keyEvent(name, state) {
const charCode = name.toKeyCode(); const charCode = name.toKeyCode();
let evt = new KeyboardEvent(state, { let evt = new KeyboardEvent(state, {
"key": name, "key": name,
@ -92,8 +99,8 @@ function keyEvent(name, state) {
"which": charCode "which": charCode
}); });
window.dispatchEvent(evt); window.dispatchEvent(evt);
} }
function mouseEvent(number, state, element, event = {"clientX": 0, "clientY" : 0, "screenX": 0, "screenY": 0}) { function mouseEvent(number, state, element, event = {"clientX": 0, "clientY" : 0, "screenX": 0, "screenY": 0}) {
element.dispatchEvent(new PointerEvent(state, { element.dispatchEvent(new PointerEvent(state, {
"button": number, "button": number,
"buttons": number, "buttons": number,
@ -102,76 +109,76 @@ function mouseEvent(number, state, element, event = {"clientX": 0, "clientY" : 0
"screenX": event.screenX, "screenX": event.screenX,
"screenY": event.screenY "screenY": event.screenY
})); }));
} }
function wheelEvent(element, delta) { function wheelEvent(element, delta) {
element.dispatchEvent(new WheelEvent("wheel", { element.dispatchEvent(new WheelEvent("wheel", {
"wheelDeltaY": delta "wheelDeltaY": delta
})); }));
} }
function setButtonVisibility(pointerLocked) { function setButtonVisibility(pointerLocked) {
let inGameStyle = document.getElementById('inGameStyle'); let inGameStyle = document.getElementById('inGameStyle');
let inMenuStyle = document.getElementById('inMenuStyle'); let inMenuStyle = document.getElementById('inMenuStyle');
inGameStyle.disabled = pointerLocked; inGameStyle.disabled = pointerLocked;
inMenuStyle.disabled = !pointerLocked; inMenuStyle.disabled = !pointerLocked;
} }
// POINTERLOCK // POINTERLOCK
// When requestpointerlock is called, this dispatches an event, saves the requested element to window.fakelock, and unhides the touch controls // When requestpointerlock is called, this dispatches an event, saves the requested element to window.fakelock, and unhides the touch controls
window.fakelock = null; window.fakelock = null;
Object.defineProperty(Element.prototype, "requestPointerLock", { Object.defineProperty(Element.prototype, "requestPointerLock", {
value: function() { value: function() {
window.fakelock = this window.fakelock = this
document.dispatchEvent(new Event('pointerlockchange')); document.dispatchEvent(new Event('pointerlockchange'));
setButtonVisibility(true); setButtonVisibility(true);
return true return true
} }
}); });
// Makes pointerLockElement return window.fakelock // Makes pointerLockElement return window.fakelock
Object.defineProperty(Document.prototype, "pointerLockElement", { Object.defineProperty(Document.prototype, "pointerLockElement", {
get: function() { get: function() {
return window.fakelock; return window.fakelock;
} }
}); });
// When exitPointerLock is called, this dispatches an event, clears the // When exitPointerLock is called, this dispatches an event, clears the
Object.defineProperty(Document.prototype, "exitPointerLock", { Object.defineProperty(Document.prototype, "exitPointerLock", {
value: function() { value: function() {
window.fakelock = null window.fakelock = null
document.dispatchEvent(new Event('pointerlockchange')); document.dispatchEvent(new Event('pointerlockchange'));
setButtonVisibility(false); setButtonVisibility(false);
return true return true
} }
}); });
// FULLSCREEN // FULLSCREEN
window.fakefull = null; window.fakefull = null;
// Stops the client from crashing when fullscreen is requested // Stops the client from crashing when fullscreen is requested
Object.defineProperty(Element.prototype, "requestFullscreen", { Object.defineProperty(Element.prototype, "requestFullscreen", {
value: function() { value: function() {
window.fakefull = this window.fakefull = this
document.dispatchEvent(new Event('fullscreenchange')); document.dispatchEvent(new Event('fullscreenchange'));
return true return true
} }
}); });
Object.defineProperty(document, "fullscreenElement", { Object.defineProperty(document, "fullscreenElement", {
get: function() { get: function() {
return window.fakefull; return window.fakefull;
} }
}); });
Object.defineProperty(Document.prototype, "exitFullscreen", { Object.defineProperty(Document.prototype, "exitFullscreen", {
value: function() { value: function() {
window.fakefull = null window.fakefull = null
document.dispatchEvent(new Event('fullscreenchange')); document.dispatchEvent(new Event('fullscreenchange'));
return true return true
} }
}); });
// FILE UPLOADING // FILE UPLOADING
// Safari doesn't recognize the element.click() used to display the file uploader as an action performed by the user, so it ignores it. // Safari doesn't recognize the element.click() used to display the file uploader as an action performed by the user, so it ignores it.
// This hijacks the element.createElement() function to add the file upload to the DOM, so the user can manually press the button again. // This hijacks the element.createElement() function to add the file upload to the DOM, so the user can manually press the button again.
const _createElement = document.createElement; const _createElement = document.createElement;
document.createElement = function(type, ignore) { document.createElement = function(type, ignore) {
this._createElement = _createElement; this._createElement = _createElement;
var element = this._createElement(type); var element = this._createElement(type);
if(type == "input" && !ignore) { // We set the ingore flag to true when we create the hiddenInput if(type == "input" && !ignore) { // We set the ingore flag to true when we create the hiddenInput
@ -190,28 +197,28 @@ document.createElement = function(type, ignore) {
document.body.appendChild(element); document.body.appendChild(element);
} }
return element; return element;
} }
// Lazy way to hide touch controls through CSS. // Lazy way to hide touch controls through CSS.
let inGameStyle = document.createElement("style"); let inGameStyle = document.createElement("style");
inGameStyle.id = "inGameStyle"; inGameStyle.id = "inGameStyle";
inGameStyle.textContent = ` inGameStyle.textContent = `
.inGame { .inGame {
display: none; display: none;
}`; }`;
document.documentElement.appendChild(inGameStyle); document.documentElement.appendChild(inGameStyle);
let inMenuStyle = document.createElement("style"); let inMenuStyle = document.createElement("style");
inMenuStyle.id = "inMenuStyle"; inMenuStyle.id = "inMenuStyle";
inMenuStyle.textContent = ` inMenuStyle.textContent = `
.inMenu { .inMenu {
display: none; display: none;
}`; }`;
document.documentElement.appendChild(inMenuStyle); document.documentElement.appendChild(inMenuStyle);
// The canvas is created by the client after it finishes unzipping and loading. When the canvas is created, this applies any necessary event listeners and creates buttons // The canvas is created by the client after it finishes unzipping and loading. When the canvas is created, this applies any necessary event listeners and creates buttons
function waitForElm(selector) { function waitForElm(selector) {
return new Promise(resolve => { return new Promise(resolve => {
if (document.querySelector(selector)) { if (document.querySelector(selector)) {
return resolve(document.querySelector(selector)); return resolve(document.querySelector(selector));
@ -227,8 +234,8 @@ function waitForElm(selector) {
subtree: true subtree: true
}); });
}); });
} }
function createTouchButton(buttonClass, buttonDisplay, elementName) { function createTouchButton(buttonClass, buttonDisplay, elementName) {
var touchButton = document.createElement(elementName ?? 'button', true); var touchButton = document.createElement(elementName ?? 'button', true);
touchButton.classList.add(buttonClass); touchButton.classList.add(buttonClass);
touchButton.classList.add(buttonDisplay); touchButton.classList.add(buttonDisplay);
@ -236,11 +243,11 @@ function createTouchButton(buttonClass, buttonDisplay, elementName) {
touchButton.addEventListener("touchmove", function(e){e.preventDefault()}, false); touchButton.addEventListener("touchmove", function(e){e.preventDefault()}, false);
touchButton.addEventListener("contextmenu", function(e){e.preventDefault()}); touchButton.addEventListener("contextmenu", function(e){e.preventDefault()});
return touchButton; return touchButton;
} }
waitForElm('canvas').then(() => {insertCanvasElements()}); waitForElm('canvas').then(() => {insertCanvasElements()});
function insertCanvasElements() { function insertCanvasElements() {
// Translates touchmove events to mousemove events when inGame, and touchmove events to wheele events when inMenu // Translates touchmove events to mousemove events when inGame, and touchmove events to wheele events when inMenu
var canvas = document.querySelector('canvas'); var canvas = document.querySelector('canvas');
canvas.addEventListener("touchstart", function(e) { canvas.addEventListener("touchstart", function(e) {
@ -644,10 +651,10 @@ function insertCanvasElements() {
keyEvent("3", "keyup"); keyEvent("3", "keyup");
}, false); }, false);
document.body.appendChild(coordinatesButton); document.body.appendChild(coordinatesButton);
} }
// CSS for touch screen buttons, along with fixing iOS's issues with 100vh ignoring the naviagtion bar, and actually disabling zoom because safari ignores user-scalable=no :( // CSS for touch screen buttons, along with fixing iOS's issues with 100vh ignoring the naviagtion bar, and actually disabling zoom because safari ignores user-scalable=no :(
let customStyle = document.createElement("style"); let customStyle = document.createElement("style");
customStyle.textContent = ` customStyle.textContent = `
.mobileControl, .mobileControl:active, .mobileControl.active{ .mobileControl, .mobileControl:active, .mobileControl.active{
position: absolute; position: absolute;
width: 10vh; width: 10vh;
@ -862,4 +869,5 @@ customStyle.textContent = `
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6NDY6NDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+3ivAWwAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAAM9JREFUOI2t1DEKwjAYhuG34mF6ja4iUicDIjjXScSDFIReIc4ijr1Cu/USJkeoi6lVm9TUfksgJA/fX0pgpATPtf7XCQwipRwkCCGaRrVBqqryQsIwbLCJ2fRFPu9MHOc6czzsyfP8a3/qAwDM5guiKPKHDABwud6s55yjFUX5hmRZ1tmmFwJYb7bOJibW0dptpDyjtbK2cUImWisAJ2KFiqL8GXBCfWN0pfnY5nf3adG+EwB1HC8RYuXVwCRJdih1f42WpqdBULsRjPAePQBKokYW6r1DQwAAAABJRU5ErkJggg=="); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAFy2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICBleGlmOlBpeGVsWERpbWVuc2lvbj0iMTgiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSIxOCIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB0aWZmOkltYWdlTGVuZ3RoPSIxOCIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOCIKICAgdGlmZjpSZXNvbHV0aW9uVW5pdD0iMiIKICAgdGlmZjpYUmVzb2x1dGlvbj0iNzIvMSIKICAgdGlmZjpZUmVzb2x1dGlvbj0iNzIvMSIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjQtMDYtMDFUMTk6NDY6NDUtMDU6MDAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTozMzoxNi0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgeG1wTU06YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgeG1wTU06c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgeG1wTU06d2hlbj0iMjAyNC0wNS0zMVQwOTo1MTo0My0wNTowMCIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjUuMSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyNC0wNi0wMVQxOTo0Njo0NS0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+3ivAWwAAAYBpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHPK0RRFMc/ZvyK0SgWFhaThpWRHzWxUUZCSRqjDDYz1/xQM+P13pNkq2wVJTZ+LfgL2CprpYiUrGdNbJie8+ZNjWTO7Z77ud97zunec8EVyaisUd0L2Zyph8dDvvnogq8uTy1VuKnHH1OGNjIzM0VF+3yUWLH7gF2rcty/1ricMBRU1QsPK003hSeEp9ZNzeY94VaVji0LXwh363JB4Qdbjzuctznl8LfNeiQ8Cq5mYV/qF8d/sUrrWWF5Of5sZk2V7mO/xJPIzc3K2iGzHYMw44TwMckYowTpY0h8kAD99MiOCvm9xfxpViVXidfYQGeFFGlMukVdk+oJWZOiJ2Rk2LD7/7evRnKg36nuCUHNq2W9d0LdLhR2LOvrxLIKp+B+getcOX/1GAY/RN8pa/4j8G7B5U1Zi+/D1Ta0PWsxPVaU3DJdySS8nUNTFFruoGHR6VnpnLMniGzKV93CwSF0Sbx36QfytWex+zpwggAAAAlwSFlzAAALEwAACxMBAJqcGAAAAM9JREFUOI2t1DEKwjAYhuG34mF6ja4iUicDIjjXScSDFIReIc4ijr1Cu/USJkeoi6lVm9TUfksgJA/fX0pgpATPtf7XCQwipRwkCCGaRrVBqqryQsIwbLCJ2fRFPu9MHOc6czzsyfP8a3/qAwDM5guiKPKHDABwud6s55yjFUX5hmRZ1tmmFwJYb7bOJibW0dptpDyjtbK2cUImWisAJ2KFiqL8GXBCfWN0pfnY5nf3adG+EwB1HC8RYuXVwCRJdih1f42WpqdBULsRjPAePQBKokYW6r1DQwAAAABJRU5ErkJggg==");
} }
`; `;
document.documentElement.appendChild(customStyle); document.documentElement.appendChild(customStyle);
}