mirror of
https://github.com/FlamedDogo99/EaglerMobile.git
synced 2024-11-26 18:44:20 -08:00
Merge pull request #20 from FlamedDogo99/chat-fix
Keyboard fixes and other improvements
This commit is contained in:
commit
90f5b20871
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
|
||||
[![Releases](https://img.shields.io/github/v/release/FlamedDogo99/EaglerMobile?style=flat-square&logo=github&logoColor=white&label=GitHub&color=181716)](https://github.com/FlamedDogo99/EaglerMobile/releases)
|
||||
[![Releases](https://img.shields.io/github/v/release/FlamedDogo99/EaglerMobile?style=flat-square&logo=github&logoColor=white&label=GitHub&color=181717)](https://github.com/FlamedDogo99/EaglerMobile/releases)
|
||||
[![Eaglercraft.com](https://img.shields.io/endpoint?url=https%3A%2F%2Fcellshield.info%2Fgs%3FspreadSheetId%3D1rkNuoBtzxp2m_psnMBCyaWw2BujITghrqE_2cKB6eW0%26cellRange%3DB3&style=flat-square&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABAlBMVEUAAAAAAAAwIRZPNyYkGRA6KBs9LB5jRjB5VzxLNiVoqD4%2FaCVsrEJbQCwrRBqXxmdmpzwnQBZ3t01zs0lxsUdwr0Zqqj9fnzRblzSGtVZlpTtjozhhoTdDbChHMSIuSR11tUssLCygz3CUw2SSwWGKuVp8vFJggEFgmzhcnDI7Tic1JRij03ONvF1YWFiBsFF5uU56p0xGRkZvUDdWdDZLdi9KdC5AUyxVlStGcCs0SCEkPRWcyW2IyF6Is1uDw1mHtleDrlZ9rE10okZnhUZYmC5Xly0%2BUSo5Yx2q2XqRvGSPvl9uqUdurkRvm0NAQEBZdTxaeTtUfjc3NzdPiig4SyTTBcNjAAAAAnRSTlMAJEqQKekAAAIRSURBVDjLfI7nbsIwFIUxxKKKrWzbmU1ISEIoCJDYo%2BzRPdS%2B%2F6vUJW3VJc79%2BX333lP4FQAKpwIug2QJTmBbruIFMcH%2F2HQVhbE4SYJgCv7ie8ZW9t3MmWHDiGNdBD9xjYlbQ3YVJzKUqxU1bMO9Bt%2FwbkdItSqGiqxZVhSFoaZROnwBOe4Mqavzds6woViMzjNXxvhG47Img3cBoVqHBhHTSZat5cA1CF6ElqMlRFVz4anr1USMRR5qaI68bVuMkI4kSZOjcLHpdj1kiqKitFr2lNrOvG3dqmrd93PhYeONPIQeTRHreowbzWbbVCUu7PfCUTiHsN%2BHvTT1zFam63ZzORkLkuDXBaGYvyif9SHsPac9hNZ6w1Rfx2PBF7jzJZRhuYTgKIUHD%2FFqA5UznkrlQyiV%2BUA4gvCAoC8MBny%2FyNc%2FL7wVTja3DsMwDAZM%2FVCHrBCgC%2BSQjvL23%2BVRtg9FgbZCYh%2F0kYyA6Elc6aobOpSsGkNP1QIOMgEHbqTDHhYlpEZFbGBGEPQJjFBJbTHGBqRvCxIAw9RWRV%2FnBgiHp0%2BjDv9rpF%2FfEWwCmdBR1uHC%2BlrAqQ4VTzRXrbWKxwzaDhIyb6qAFtcQoPb%2BhickTkGqK%2BcEpabZdmhkzdEMBEREhQh%2F%2BSuPy%2BEU4WuAsK1%2BQQAKYVi0%2B1K%2FI05yWOd%2F2K0TSLmfX7bzyHjL%2Frn%2B%2FymZL56MQephAAAAAElFTkSuQmCC&label=eaglercraft.com&color=%237fad55)](https://eaglercraft.com)
|
||||
[![License](https://img.shields.io/github/license/FlamedDogo99/EaglerMobile?style=flat-square)](https://github.com/FlamedDogo99/EaglerMobile/blob/main/LICENSE)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,13 +6,13 @@
|
|||
// @downloadURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
|
||||
// @license Apache License 2.0 - http://www.apache.org/licenses/
|
||||
// @match https://eaglercraft.com/mc/*
|
||||
// @version 3.0.3
|
||||
// @version 3.0.4
|
||||
// @updateURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
|
||||
// @run-at document-start
|
||||
// @grant unsafeWindow
|
||||
// ==/UserScript==
|
||||
|
||||
// This is generally a bad practice, but we need to run scripts in the main context before the DOM loads. Because we are only matching eaglercraft.com, the use of unsafeWindow should be safe to use.
|
||||
// This is generally a bad practice, but we need to run scripts in the main context before the DOM loads. Because we are only matching eaglercraft.com, unsafeWindow should be safe to use.
|
||||
// If someone knows a better way of doing this, please create an issue
|
||||
try {
|
||||
unsafeWindow.console.warn("DANGER: This userscript is using unsafeWindow. Unsafe websites could potentially use this to gain access to data and other content that the browser normally wouldn't allow!")
|
||||
|
@ -24,7 +24,7 @@ try {
|
|||
value: window
|
||||
});
|
||||
}
|
||||
|
||||
// To-do: remove the mobile check is implement the dynamic enabling and disabling of individual features
|
||||
function isMobile() {
|
||||
try {
|
||||
document.createEvent("TouchEvent");
|
||||
|
@ -37,43 +37,44 @@ if(!isMobile()) {
|
|||
alert("WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!");
|
||||
}
|
||||
|
||||
clientWindow.keyboardEnabled = false;
|
||||
clientWindow.crouchLock = false;
|
||||
clientWindow.sprintLock = false;
|
||||
clientWindow.keyboardFix = false;
|
||||
clientWindow.inputFix = false;
|
||||
clientWindow.blockNextInput = false;
|
||||
clientWindow.crouchLock = false; // Used for crouch mobile control
|
||||
clientWindow.sprintLock = false; // Used for sprint mobile control
|
||||
clientWindow.keyboardFix = false; // keyboardFix ? "Standard Keyboard" : "Compatibility Mode"
|
||||
clientWindow.inputFix = false; // If true, Duplicate Mode
|
||||
clientWindow.blockNextInput = false; // Used for Duplicate Mode
|
||||
clientWindow.hiddenInputFocused = false;
|
||||
|
||||
// Used for changing touchmove events to mousemove events
|
||||
var previousTouchX = null;
|
||||
var previousTouchY = null;
|
||||
var startTouchX = null;
|
||||
|
||||
// better charCodeAt 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() {
|
||||
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];
|
||||
}
|
||||
// Ignores keydown events that don't have the isValid parameter set to true
|
||||
// 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
|
||||
const _addEventListener = EventTarget.prototype.addEventListener;
|
||||
Object.defineProperty(EventTarget.prototype, "addEventListener", {
|
||||
value: function (type, fn, ...rest) {
|
||||
if(type == 'keydown') {
|
||||
if(type == 'keydown') { // Check if a keydown event is being added
|
||||
_addEventListener.call(this, type, function(...args) {
|
||||
if(!args[0].isValid && clientWindow.keyboardFix) {
|
||||
return;
|
||||
if(!args[0].isValid && clientWindow.keyboardFix) { // Inject check for isValid flag and Compatibility Mode
|
||||
return; // When we are in Compatibility Mode, standard key presses will be ignored
|
||||
}
|
||||
return fn.apply(this, args);
|
||||
return fn.apply(this, args); // Appends the rest of the function specified by addEventListener
|
||||
}, ...rest);
|
||||
} else {
|
||||
} else { // If it's not a keydown event, behave like normal
|
||||
_addEventListener.call(this, type, fn, ...rest);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Allows typing in #hiddenInput
|
||||
// Overrides preventDefault, because on some (Android) devices you couldn't type into hiddenInput
|
||||
const _preventDefault = Event.prototype.preventDefault;
|
||||
Event.prototype.preventDefault = function(shouldBypass) {
|
||||
if(document.activeElement.id != "hiddenInput" || shouldBypass) {
|
||||
if(document.activeElement.id != "hiddenInput" || shouldBypass) { // activeElement is what element is currently focused
|
||||
this._preventDefault = _preventDefault;
|
||||
this._preventDefault();
|
||||
}
|
||||
|
@ -203,7 +204,7 @@ inMenuStyle.textContent = `
|
|||
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
|
||||
// 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) {
|
||||
return new Promise(resolve => {
|
||||
if (document.querySelector(selector)) {
|
||||
|
@ -231,16 +232,6 @@ function createTouchButton(buttonClass, buttonDisplay, elementName) {
|
|||
return touchButton;
|
||||
}
|
||||
|
||||
function toggleKeyboard() {
|
||||
const keyboardInput = document.getElementById('hiddenInput');
|
||||
if (clientWindow.keyboardEnabled) {
|
||||
clientWindow.keyboardEnabled = false;
|
||||
keyboardInput.blur();
|
||||
} else {
|
||||
clientWindow.keyboardEnabled = true;
|
||||
keyboardInput.select();
|
||||
}
|
||||
}
|
||||
|
||||
waitForElm('canvas').then(() => {insertCanvasElements()});
|
||||
function insertCanvasElements() {
|
||||
|
@ -378,67 +369,105 @@ function insertCanvasElements() {
|
|||
exitButton.addEventListener("touchstart", function(e){keyEvent("`", "keydown")}, false);
|
||||
exitButton.addEventListener("touchend", function(e){keyEvent("`", "keyup")}, false);
|
||||
document.body.appendChild(exitButton);
|
||||
// input for keyboard button
|
||||
// ---Input Handling---
|
||||
// This code is a mess, specifically because Android is so so SO inconsistent with how it handles the keyboard
|
||||
// Some keyboards dispatch key events, some directly append text, and none of them meet the most basic standards supported by most other devices
|
||||
// This mess is my attempt at dealing with that, and it will most likely only ever be triggered by Android
|
||||
//
|
||||
// It has three main modes.
|
||||
// 1) Standard keyboard mode:
|
||||
// This mode keeps the hiddenInput empty, saves the last key press, and on every keypress checks if it the keys are being pressed incorrectly.
|
||||
// If there is a problem, it switches to compatibility mode, using beforeinput and input events instead of keydown and keyup
|
||||
// 2) Compatibility mode:
|
||||
// This most is most likely going to be used by Android, because Android only every dispatches keyCode 229 for any keypress
|
||||
// When we enter this mode, we grab the last known key press and redispatch it, and programatically dispatch key events by reading e.inputType and e.data from beforeinput
|
||||
// Unfortunately, Android is weird with this as well. Sometimes it only dispatches insertCompositionText events, and sometimes it gives the correct inputTypes as well
|
||||
// Additionally, programmatically setting the input's text contents (BECAUSE ANDROID IGNORES PREVENTDEFAULT AGHHHHH) dispatches a repeat of the previous event
|
||||
// Luckily, we can check if this happens when we first create the input, which necessitates the third mode:
|
||||
// 3) Duplicate mode:
|
||||
// If we are getting duplicate inputs, this mode ignores every other input if it matches the state saved in clientWindow.previousKey
|
||||
// If users make it to this mode and still are having issues, it may be best just to remove support for their device
|
||||
// ---Input Handling---
|
||||
let hiddenInput = document.createElement('input', true);
|
||||
hiddenInput.id = "hiddenInput"
|
||||
hiddenInput.classList.add("inMenu")
|
||||
// We are hiding the text input behind button because opacity was causing problems.
|
||||
hiddenInput.style.cssText = "position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index:-10;color: transparent;text-shadow: 0 0 0 black;";
|
||||
hiddenInput.value = " " //Allows delete to be detected before input is changed
|
||||
hiddenInput.addEventListener("input", function(e) {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault(true);
|
||||
let inputData = e.data == null ? "delete" : e.data.slice(-1);
|
||||
if(!clientWindow.lastKey) { // If we get an event before any keys have been pressed, we know that setting the hiddenInput creates duplicate input events, so we can apply the fix
|
||||
hiddenInput.style.cssText = "position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index: -10;color: transparent;text-shadow: 0 0 0 black;"; // We hide the input behind a key because display: none and opacity:0 causes issues
|
||||
hiddenInput.addEventListener("beforeinput", function(e) { // For some reason beforeinput doesn't have the same deletion problems that input has on Android
|
||||
e.stopImmediatePropagation(); // Android ignores this and the prevent default, so this will probably be removed in the future
|
||||
e.preventDefault(true); // We pass a value because we've hijacked the prevent default function to have a "should bypass" boolean value
|
||||
let inputData = (e.inputType == "insertLineBreak") ? "return" : (e.data == null ? "delete" : e.data.slice(-1)); // Saves the last key press.
|
||||
if(!clientWindow.lastKey) { // When we first set hiddenInput's text contents to " " we can use this to check if Duplicate Mode is needed
|
||||
clientWindow.console.warn("Enabling blocking duplicate key events. Some functionality may be lost.")
|
||||
clientWindow.inputFix = true;
|
||||
}
|
||||
clientWindow.console.log(`Received input by ${e.inputType}: ${e.data} -> ${inputData}`);
|
||||
|
||||
hiddenInput.value = " ";
|
||||
if(clientWindow.keyboardFix) {
|
||||
const sliceInputType = e.inputType.slice(0,1); // This is a really dumb way to do this because it's not future-proof, but its the easiest way to deal with Android
|
||||
if(sliceInputType== 'i') {
|
||||
const isDuplicate = (clientWindow.lastKey == inputData) && clientWindow.blockNextInput && clientWindow.inputFix;
|
||||
if(isDuplicate) {
|
||||
clientWindow.blockNextInput = false;
|
||||
} else {
|
||||
let isShift = (inputData.toLowerCase() != inputData);
|
||||
if(isShift) {
|
||||
keyEvent("shift", "keydown")
|
||||
keyEvent(inputData, "keydown");
|
||||
keyEvent(inputData, "keyup");
|
||||
keyEvent("shift", "keyup")
|
||||
if(e.inputType == "insertLineBreak") { // Detects return key press
|
||||
keyEvent("enter", "keydown");
|
||||
keyEvent("enter", "keyup");
|
||||
} else {
|
||||
const sliceInputType = e.inputType.slice(0,1); // Android doesn't constiently dispatch the correct inputType, but most of them either start with i for insert or d for delete, so this dumb solution should be good enough.
|
||||
if(sliceInputType== 'i' && e.data) { // Android sometimes always dispatches insertCompositionText inputTypes, so checking that e.data isn't null is necessary
|
||||
const isDuplicate = (clientWindow.lastKey == inputData) && clientWindow.blockNextInput && clientWindow.inputFix;
|
||||
if(isDuplicate) { // If our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input
|
||||
clientWindow.blockNextInput = false;
|
||||
} else {
|
||||
keyEvent(inputData, "keydown");
|
||||
keyEvent(inputData, "keyup");
|
||||
let isShift = (inputData.toLowerCase() != inputData);
|
||||
if(isShift) { // The Eaglerclient only uses e.key, e.keyCode and e.which, so we have to dispatch the shift key event separately
|
||||
keyEvent("shift", "keydown");
|
||||
keyEvent(inputData, "keydown");
|
||||
keyEvent(inputData, "keyup");
|
||||
keyEvent("shift", "keyup");
|
||||
} else {
|
||||
keyEvent(inputData, "keydown");
|
||||
keyEvent(inputData, "keyup");
|
||||
}
|
||||
clientWindow.blockNextInput = true;
|
||||
}
|
||||
clientWindow.blockNextInput = true;
|
||||
} else if (sliceInputType == 'd' || !e.data) {
|
||||
keyEvent("backspace", "keydown");
|
||||
keyEvent("backspace", "keyup");
|
||||
clientWindow.blockNextInput = false; // If we delete a character, there couldn't be a duplicate of the previous key press
|
||||
}
|
||||
} else if (sliceInputType == 'd') {
|
||||
keyEvent("backspace", "keydown");
|
||||
keyEvent("backspace", "keyup");
|
||||
clientWindow.blockNextInput = false;
|
||||
}
|
||||
}
|
||||
clientWindow.lastKey = inputData
|
||||
clientWindow.lastKey = inputData // Saves the last key pressed
|
||||
hiddenInput.value = " " //This previously allowed us to have a character to delete, but beforeinput doesn't require this. This does allow us to check wether Duplicate Mode is necessary though
|
||||
|
||||
|
||||
}, false);
|
||||
hiddenInput.addEventListener("keydown", function(e) {
|
||||
hiddenInput.addEventListener("input", function(e) { // Since we are using beforeInput for input detection, setting the text contents of hiddenInput causes weird behavior, so we use input instead
|
||||
if (hiddenInput.value != " ") { // Avoid updating it if not needed so Duplicate Mode doesn't throw a fit
|
||||
hiddenInput.value = " ";
|
||||
}
|
||||
}, false);
|
||||
hiddenInput.addEventListener("keydown", function(e) { // Enables Compatibility Mode if we receive an invalid key press event
|
||||
if((e.keyCode == 229 || e.which == 229) && !clientWindow.keyboardFix) {
|
||||
clientWindow.console.warn("Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.")
|
||||
clientWindow.keyboardFix = true;
|
||||
if(clientWindow.lastKey) {
|
||||
if(clientWindow.lastKey) { // Resend the last saved key press (which is being tracked by the beforeinput event listener) so the transition to Compatibility Mode isn't noticeable
|
||||
keyEvent(clientWindow.lastKey, "keydown");
|
||||
keyEvent(clientWindow.lastKey, "keyup");
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
hiddenInput.addEventListener("blur", function(e) {
|
||||
clientWindow.hiddenInputFocused = false;
|
||||
});
|
||||
document.body.appendChild(hiddenInput);
|
||||
let keyboardButton = createTouchButton("keyboardButton", "inMenu");
|
||||
keyboardButton.style.cssText = "top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;"
|
||||
keyboardButton.addEventListener("touchstart", function(e){e.preventDefault();hiddenInput.blur()}, false);
|
||||
keyboardButton.addEventListener("touchend", function(e){e.preventDefault();toggleKeyboard()}, false);
|
||||
keyboardButton.addEventListener("touchstart", function(e){
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
keyboardButton.addEventListener("touchend", function(e){
|
||||
e.preventDefault();
|
||||
if(clientWindow.hiddenInputFocused) {
|
||||
hiddenInput.blur()
|
||||
} else {
|
||||
hiddenInput.select()
|
||||
clientWindow.hiddenInputFocused = true;
|
||||
}
|
||||
}, false);
|
||||
document.body.appendChild(keyboardButton);
|
||||
let placeButton = createTouchButton("placeButton", "inGame");
|
||||
placeButton.style.cssText = "right:0vh;bottom:20vh;"
|
||||
|
@ -495,7 +524,7 @@ function insertCanvasElements() {
|
|||
document.body.appendChild(pauseButton);
|
||||
let chatButton = createTouchButton("chatButton", "inGame");
|
||||
chatButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;"
|
||||
chatButton.addEventListener("touchstart", function(e){keyEvent("t", "keydown")}, false);
|
||||
chatButton.addEventListener("touchstart", function(e){keyEvent("t", "keydown")}, false); // For some reason dispatching a keyup event for this closes the chat, which is really weird
|
||||
document.body.appendChild(chatButton);
|
||||
let perspectiveButton = createTouchButton("perspectiveButton", "inGame");
|
||||
perspectiveButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;"
|
||||
|
@ -573,6 +602,7 @@ customStyle.textContent = `
|
|||
border: none;
|
||||
}
|
||||
html, body, canvas {
|
||||
height: 100svh !important;
|
||||
height: -webkit-fill-available !important;
|
||||
touch-action: pan-x pan-y;
|
||||
-webkit-touch-callout: none;
|
||||
|
@ -739,4 +769,4 @@ 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==");
|
||||
}
|
||||
`;
|
||||
document.documentElement.appendChild(customStyle);
|
||||
document.documentElement.appendChild(customStyle);
|
Loading…
Reference in New Issue
Block a user