Cubic Engine

Enhance your Experience

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Cubic Engine
// @version      6.0.2
// @description  Enhance your Experience
// @namespace    drawaria.modded.fullspec
// @homepage     https://drawaria.online/profile/?uid=63196790-c7da-11ec-8266-c399f90709b7
// @author       ≺ᴄᴜʙᴇ³≻
// @match        https://drawaria.online/
// @match        https://drawaria.online/test
// @match        https://drawaria.online/room/*
// @icon         https://drawaria.online/avatar/cache/e53693c0-18b1-11ec-b633-b7649fa52d3f.jpg
// @grant        none
// @license      GNU GPLv3
// @run-at       document-end
// ==/UserScript==

(function () {
  (function CodeMaid(callback) {
    class TypeChecker {
      constructor() {}
      isArray(value) {
        return this.isA('Array', value);
      }
      isObject(value) {
        return !this.isUndefined(value) && value !== null && this.isA('Object', value);
      }
      isString(value) {
        return this.isA('String', value);
      }
      isNumber(value) {
        return this.isA('Number', value);
      }
      isFunction(value) {
        return this.isA('Function', value);
      }
      isAsyncFunction(value) {
        return this.isA('AsyncFunction', value);
      }
      isGeneratorFunction(value) {
        return this.isA('GeneratorFunction', value);
      }
      isTypedArray(value) {
        return (
          this.isA('Float32Array', value) ||
          this.isA('Float64Array', value) ||
          this.isA('Int16Array', value) ||
          this.isA('Int32Array', value) ||
          this.isA('Int8Array', value) ||
          this.isA('Uint16Array', value) ||
          this.isA('Uint32Array', value) ||
          this.isA('Uint8Array', value) ||
          this.isA('Uint8ClampedArray', value)
        );
      }
      isA(typeName, value) {
        return this.getType(value) === '[object ' + typeName + ']';
      }
      isError(value) {
        if (!value) {
          return false;
        }

        if (value instanceof Error) {
          return true;
        }

        return typeof value.stack === 'string' && typeof value.message === 'string';
      }
      isUndefined(obj) {
        return obj === void 0;
      }
      getType(value) {
        return Object.prototype.toString.apply(value);
      }
    }

    class DOMCreate {
      #validate;
      constructor() {
        this.#validate = new TypeChecker();
      }
      exportNodeTree(node = document.createElement('div')) {
        let referenceTolocalThis = this;

        let json = {
          nodeName: node.nodeName,
          attributes: {},
          children: [],
        };

        Array.from(node.attributes).forEach(function (attribute) {
          json.attributes[attribute.name] = attribute.value;
        });

        if (node.children.length <= 0) {
          json.children.push(node.textContent.replaceAll('\t', ''));
          return json;
        }

        Array.from(node.children).forEach(function (childNode) {
          json.children.push(referenceTolocalThis.exportNodeTree(childNode));
        });

        return json;
      }

      importNodeTree(json = { nodeName: '', attributes: {}, children: [] }) {
        let referenceTolocalThis = this;

        if (referenceTolocalThis.#validate.isString(json)) {
          return this.TextNode(json);
        }

        let node = this.Tree(json.nodeName, json.attributes);

        json.children.forEach(function (child) {
          node.appendChild(referenceTolocalThis.importNodeTree(child));
        });

        return node;
      }

      Element() {
        return document.createElement.apply(document, arguments);
      }
      TextNode() {
        return document.createTextNode.apply(document, arguments);
      }
      Tree(type, attrs = {}, childrenArrayOrVarArgs) {
        const el = this.Element(type);
        let children;
        if (this.#validate.isArray(childrenArrayOrVarArgs)) {
          children = childrenArrayOrVarArgs;
        } else {
          children = [];

          for (let i = 2; i < arguments.length; i++) {
            children.push(arguments[i]);
          }
        }

        for (let i = 0; i < children.length; i++) {
          const child = children[i];

          if (typeof child === 'string') {
            el.appendChild(this.TextNode(child));
          } else {
            if (child) {
              el.appendChild(child);
            }
          }
        }
        for (const attr in attrs) {
          if (attr == 'className') {
            el[attr] = attrs[attr];
          } else {
            el.setAttribute(attr, attrs[attr]);
          }
        }

        el.appendAll = function (...nodes) {
          nodes.forEach((node) => {
            el.appendChild(node);
          });
        };

        return el;
      }
    }

    class CookieManager {
      constructor() {}
      set(name, value = '', maxAgeDays = 365) {
        const maxAge = Math.max(0, Math.floor(Number(maxAgeDays) * 86400));
        document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=/; SameSite=Lax`;
      }
      get(name) {
        const nameEQ = encodeURIComponent(name) + '=';
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
          let c = cookies[i];
          while (c.charAt(0) == ' ') c = c.substring(1, c.length);
          if (c.indexOf(nameEQ) == 0) {
            const value = c.substring(nameEQ.length, c.length);
            try {
              return decodeURIComponent(value);
            } catch (error) {
              return value;
            }
          }
        }
        return null;
      }
      clear(name) {
        document.cookie = `${encodeURIComponent(name)}=; Max-Age=0; Path=/; SameSite=Lax`;
      }
    }

    class DocumentCleaner {
      document;
      constructor() {
        this.document = new DOMCreate();
      }
      scripts(remove = true) {
        try {
          let array = document.querySelectorAll('script[src]:not([data-codemaid="ignore"])');
          array.forEach((script) => {
            if (script.src != '') document.head.appendChild(script);
          });
        } catch (error) {
          console.error(error);
        }

        try {
          let unifiedScript = this.document.Tree('script');

          let scripts = document.querySelectorAll('script:not([src]):not([data-codemaid="ignore"])');
          let unifiedScriptContent = '';
          scripts.forEach((script) => {
            let content = script.textContent; //.replaceAll(/\s/g, '');

            unifiedScriptContent += `try{${content}}catch(e){console.warn(e);}`;
            script.remove();
          });

          unifiedScript.textContent = unifiedScriptContent;

          if (!remove) document.head.appendChild(unifiedScript);
        } catch (error) {
          console.error(error);
        }
      }
      styles(remove = false) {
        try {
          let unifiedStyles = this.document.Tree('style');
          unifiedStyles.textContent = '';

          let styles = document.querySelectorAll('style:not([data-codemaid="ignore"])');
          styles.forEach((style) => {
            unifiedStyles.textContent += style.textContent;
            style.remove();
          });
          if (!remove) document.head.appendChild(unifiedStyles);
        } catch (error) {
          console.error(error);
        }
      }
      embeds() {
        try {
          let array = document.querySelectorAll('iframe');
          array.forEach((iframe) => {
            iframe.remove();
          });
        } catch (error) {
          console.error(error);
        }
      }
    }

    class CustomGenerator {
      constructor() {}
      uuidv4() {
        return crypto.randomUUID();
      }
    }

    globalThis.typecheck = new TypeChecker();
    globalThis.cookies = new CookieManager();
    globalThis.domMake = new DOMCreate();
    globalThis.domClear = new DocumentCleaner();
    globalThis.generate = new CustomGenerator();

    // if (window.location.pathname === '/') window.location.assign('/test');
  })();

  (function CubicEngine() {
    domMake.Button = function (content) {
      let btn = domMake.Tree('button', { class: 'btn btn-outline-secondary' });
      btn.innerHTML = content;
      return btn;
    };
    domMake.Row = function () {
      return domMake.Tree('div', { class: '_row' });
    };
    domMake.IconList = function () {
      return domMake.Tree('div', { class: 'icon-list' });
    };

    const sockets = [];
    const trackedSockets = new WeakSet();
    const originalSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (...args) {
      const socket = this;
      if (!trackedSockets.has(socket)) {
        trackedSockets.add(socket);
        sockets.push(socket);
        socket.addEventListener(
          'close',
          function () {
            const pos = sockets.indexOf(socket);
            if (pos !== -1) sockets.splice(pos, 1);
          },
          { once: true }
        );
      }
      return originalSend.call(socket, ...args);
    };

    const identifier = '🧊';

    class Stylizer {
      constructor() {
        this.element = domMake.Tree('style', { 'data-codemaid': 'ignore' }, []);
        document.head.appendChild(this.element);
        this.initialize();
      }

      initialize() {
        this.addRules([
          `body * {margin: 0; padding: 0; box-sizing: border-box; line-height: normal;}`,
          `#${identifier} {--CE-bg_color: var(--light); --CE-color: var(--dark); line-height: 2rem; font-size: 1rem;}`,
          `#${identifier}>details {position:relative; overflow:visible; z-index: 999; background-color: var(--CE-bg_color); border: var(--CE-color) 1px solid; border-radius: .25rem;}`,
          `#${identifier} details>summary::marker {content:"📘";}`,
          `#${identifier} details[open]>summary::marker {content:"📖";}`,
          `#${identifier} details details {margin: 1px 0; border-top: var(--CE-color) 1px solid;}`,
          `#${identifier} input.toggle[name][hidden]:not(:checked) + * {display: none !important;}`,
          `#${identifier} header>.icon {margin: 1px;}`,
          `#${identifier} header>.icon.active {color: var(--success);}`,
          `#${identifier} header>.icon:not(.active) {color:var(--danger); opacity:.6;}`,
          `#${identifier} header:not(:has([title='Unselect'] + *)) > [title='Unselect'] {display:none;}`,
          `#${identifier} .btn {padding: 0;}`,
          `#${identifier} .icon-list {display: flex; flex-flow: wrap;}`,
          `#${identifier} .nowrap {overflow-x: scroll; padding-bottom: 12px; flex-flow: nowrap;}`,
          `#${identifier} .icon {display: flex; flex: 0 0 auto; max-width: 1.6rem; min-width: 1.6rem; height: 1.6rem; border-radius: .25rem; border: 1px solid var(--CE-color); aspect-ratio: 1/1;}`,
          `#${identifier} .icon > * {margin: auto; text-align: center; max-height: 100%; max-width: 100%;}`,
          `#${identifier} .itext {text-align: center; -webkit-appearance: none; -moz-appearance: textfield;}`,
          `#${identifier} ._row {display: flex; width: 100%;}`,
          `#${identifier} ._row > * {width: 100%;}`,
          `hr {margin: 5px 0;}`,
          `.playerlist-row::after {content: attr(data-playerid); position: relative; float: right; top: -20px;}`,
          `[hidden] {display: none !important;}`,
          `.noselect {-webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; user-select: none;}`,
        ]);
      }

      addRules(rules = []) {
        let reference = this;
        rules.forEach(function (rule) {
          reference.addRule(rule);
        });
      }
      addRule(rule) {
        let TextNode = domMake.TextNode(rule);
        this.element.appendChild(TextNode);
      }
    }

    class ModBase {
      static globalListOfExtensions = [];
      static localListOfExtensions = [];
      static Styles = new Stylizer();

      static register = function (extension) {
        extension.localListOfExtensions = [];
        ModBase.globalListOfExtensions.push(extension);
        return ModBase;
      };
      static bind = function (extension, target) {
        let parent;
        if (typecheck.isFunction(target)) parent = target;
        else if (typecheck.isString(target))
          parent = ModBase.globalListOfExtensions.find((entry) => entry.name === target);
        else if (typecheck.isObject(target)) parent = target.constructor;
        else {
          console.log(typecheck.getType(target));
        }
        if (!parent) return new Error(`${parent}`);

        parent.localListOfExtensions.push(extension);
        parent.autostart = true;

        return parent;
      };
      static findGlobal = function (extensionName) {
        return ModBase.globalListOfExtensions.find((entry) => entry.name === extensionName);
      };

      #id;
      #name;
      #icon;

      htmlElements;
      children;
      parent;

      constructor(name, icon) {
        this.#id = generate.uuidv4();
        this.#name = this.constructor.name;
        this.#icon = '📦';
        this.children = [];
        this.htmlElements = {};

        this.#onStartup();

        this.setName(name || this.#name);
        this.setIcon(icon || this.#icon);
      }

      #onStartup() {
        this.#loadInterface();

        if (this.constructor.autostart)
          this.constructor.localListOfExtensions.forEach((extension) => {
            this.loadExtension(extension);
          });
      }

      #loadInterface() {
        this.htmlElements.details = domMake.Tree('details', {
          class: 'noselect',
          open: true,
          'data-reference': this.constructor.name,
        });
        this.htmlElements.summary = domMake.Tree('summary');
        this.htmlElements.header = domMake.Tree('header', { class: 'icon-list' });
        this.htmlElements.section = domMake.Tree('section');
        this.htmlElements.children = domMake.Tree('section');

        this.htmlElements.details.appendChild(this.htmlElements.summary);
        this.htmlElements.details.appendChild(this.htmlElements.header);
        this.htmlElements.details.appendChild(this.htmlElements.section);
        this.htmlElements.details.appendChild(this.htmlElements.children);

        this.htmlElements.input = domMake.Tree(
          'input',
          { type: 'radio', id: this.#id, name: 'QBit', class: 'toggle', hidden: true, title: this.#name },
          [this.#name]
        );
        this.htmlElements.label = domMake.Tree('label', { for: this.#id, class: 'icon' });

        {
          const input = this.htmlElements.input;
          const label = this.htmlElements.label;

          input.addEventListener('change', (event) => {
            this.parent?.children.forEach((child) => {
              child.htmlElements.label.classList.remove('active');
            });

            label.classList[input.checked ? 'add' : 'remove']('active');
          });

          label.classList[input.checked ? 'add' : 'remove']('active');
        }
        {
          const resetImageSelectionLabel = domMake.Tree('div', { class: 'icon', title: 'Unselect' }, [
            domMake.Tree('i', { class: 'fas fa-chevron-left' }),
          ]);
          resetImageSelectionLabel.addEventListener('click', () => {
            this.children.forEach((child) => {
              child.htmlElements.label.classList.remove('active');
              child.htmlElements.input.checked = !1;
            });
          });
          this.htmlElements.header.appendChild(resetImageSelectionLabel);
        }
      }

      loadExtension(extension, referenceHandler) {
        let activeExtension = new extension();
        activeExtension.parent = this;

        activeExtension.htmlElements.input.name = this.getName();

        if (referenceHandler) referenceHandler(activeExtension);
        else this.children.push(activeExtension);

        if (!extension.siblings) extension.siblings = [];
        extension.siblings.push(activeExtension);

        if (extension.isFavorite) {
          activeExtension.htmlElements.input.click();
          if (activeExtension.enable) activeExtension.enable();
        }

        this.htmlElements.header.appendChild(activeExtension.htmlElements.label);
        this.htmlElements.children.appendChild(activeExtension.htmlElements.input);
        this.htmlElements.children.appendChild(activeExtension.htmlElements.details);

        return activeExtension;
      }

      notify(level, message) {
        if (typeof message != 'string') {
          try {
            message = JSON.stringify(message);
          } catch (error) {
            throw error;
          }
        }

        let color = '';
        if ([5, 'error'].includes(level)) {
          color = '#dc3545';
        } else if ([4, 'warning'].includes(level)) {
          color = '#ffc107';
        } else if ([3, 'info'].includes(level)) {
          color = '#17a2b8';
        } else if ([2, 'success'].includes(level)) {
          color = '#28a745';
        } else if ([1, 'log'].includes(level)) {
          color = '#6c757d';
        } else if ([0, 'debug'].includes(level)) {
          color = 'purple';
        }

        console.log(`%c${this.#name}: ${message}`, `color: ${color}`);
        let chatmessage = domMake.Tree(
          'div',
          { class: `chatmessage systemchatmessage5`, 'data-ts': Date.now(), style: `color: ${color}` },
          [`${this.#name}: ${message}`]
        );

        let loggingContainer = document.getElementById('chatbox_messages');
        if (!loggingContainer) loggingContainer = document.body;
        loggingContainer.appendChild(chatmessage);
      }

      findGlobal(extensionName) {
        return this.referenceToBase.findGlobal(extensionName);
      }

      findLocal(extensionName) {
        return this.children.filter((child) => child.constructor.name === extensionName);
      }

      setName(name) {
        if (!name) return;

        this.#name = name;
        this.htmlElements.label.title = name;

        this.htmlElements.summary.childNodes.forEach((child) => child.remove());

        if (typecheck.isString(name)) {
          if (name.startsWith('<')) return (this.htmlElements.summary.innerHTML = name);
          name = domMake.TextNode(name);
        }

        this.htmlElements.summary.appendChild(name);
      }

      getName() {
        return this.#name;
      }

      setIcon(icon) {
        if (!icon) return;

        this.#icon = icon;

        this.htmlElements.label.childNodes.forEach((child) => child.remove());

        if (typecheck.isString(icon)) {
          if (icon.startsWith('<')) return (this.htmlElements.label.innerHTML = icon);
          icon = domMake.TextNode(icon);
        }

        this.htmlElements.label.appendChild(icon);
      }

      getIcon() {
        return this.#icon;
      }

      get referenceToBase() {
        return this.constructor.dummy1;
      }
      get referenceToMaster() {
        return this.constructor.dummy2;
      }

      _EXP_destroy(youSure = false) {
        if (!youSure) return;

        [...this.children].forEach((child) => {
          child._EXP_destroy(youSure);
        });
        this.children = null;

        const parentChildren = this.parent?.children;
        const pos = parentChildren?.indexOf(this) ?? -1;
        if (pos !== -1) {
          parentChildren.splice(pos, 1);
        }

        this.htmlElements.children.remove();
        this.htmlElements.section.remove();
        this.htmlElements.header.remove();
        this.htmlElements.summary.remove();
        this.htmlElements.details.remove();
        this.htmlElements.input.remove();
        this.htmlElements.label.remove();

        this.htmlElements = null;

        const pos2 = this.constructor.siblings?.indexOf(this) ?? -1;
        if (pos2 !== -1) {
          this.constructor.siblings.splice(pos2, 1);
        }
      }
    }

    class CubeEngine extends ModBase {
      static dummy1 = ModBase.register(this);

      constructor() {
        super('CubeEngine');
      }
    }

    class Await {
      static dummy1 = ModBase.register(this);

      #interval;
      #handler;
      #callback;
      constructor(callback, interval) {
        this.#interval = interval;
        this.#callback = callback;
      }

      call() {
        const localThis = this;
        clearTimeout(this.#handler);

        this.#handler = setTimeout(function () {
          localThis.#callback();
        }, this.#interval);
      }
    }

    globalThis[arguments[0]] = ModBase;

    return function () {
      const mount = () => {
        if (document.getElementById(identifier)) return true;

        const target = document.getElementById('accountbox');
        if (!target) return false;

        const ModMenu = new CubeEngine();
        // ModMenu.htmlElements.details.open = false;

        const container = domMake.Tree('div', { id: identifier, style: 'height: 1.6rem; flex: 0 0 auto;' });
        container.appendChild(ModMenu.htmlElements.details);
        target.after(container);
        target.after(domMake.Tree('hr'));

        globalThis['CubeEngine'] = ModMenu;
        globalThis['sockets'] = sockets;

        domClear.embeds();
        domClear.scripts();
        domClear.styles();
        console.clear();

        console.debug('Cubic Engine: Mounted Successfully');

        return true;
      };

      // Allow the remaining module IIFEs to register and bind before CubeEngine loads its children.
      queueMicrotask(() => {
        if (mount()) return;

        const observer = new MutationObserver(() => {
          if (!mount()) return;
          observer.disconnect();
        });
        observer.observe(document.documentElement, { childList: true, subtree: true });

        setTimeout(() => {
          observer.disconnect();
          if (!document.getElementById(identifier)) console.warn('Cubic Engine: #accountbox was not found; UI was not mounted.');
        }, 1000);
      });
    };
  })('QBit')();

  (function BotClient() {
    const QBit = globalThis[arguments[0]];

    const ROOM_ID_PATTERN = /^[a-f0-9-]{36}(?:\.\d+)?$/i;

    function parseServerUrl(any) {
      const value = String(any ?? '').trim();
      const roomId = parseRoomId(value);
      const serverMatch = (roomId || value).match(/(?:^|\.)(\d+)$/);
      const prefix = serverMatch ? `sv${serverMatch[1]}.` : '';
      return `wss://${prefix}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
    }

    function parseRoomId(any) {
      const value = String(any ?? '').trim();
      if (!value) return null;

      if (ROOM_ID_PATTERN.test(value)) return value;

      try {
        const url = new URL(value, window.location.origin);
        const match = url.pathname.match(/^\/room\/([^/]+)/i);
        const roomId = match ? decodeURIComponent(match[1]) : '';
        return ROOM_ID_PATTERN.test(roomId) ? roomId : null;
      } catch (error) {
        return null;
      }
    }

    function parseSocketIOEvent(prefix_length, event_data) {
      try {
        return JSON.parse(event_data.slice(prefix_length));
      } catch (error) {
        return null;
      }
    }

    // class BotClient extends QBit {
    class BotClient {
      static dummy1 = QBit.register(this);

      // constructor(name = '', avatar = []) {
      constructor(name = '☠️', avatar = ['_', '0']) {
        // super(name, `<img src="${parseAvatarURL(avatar)}">`);

        this.name = name;
        this.avatar = avatar;
        this.attributes = { spawned: false, rounded: false, status: false };

        this.url = '';
        this.socket = null;
        this.interval_id = 0;
        this.interval_ms = 25000;

        this.room = {
          id: null,
          config: null,
          type: 2,
          players: [],
        };

        this.customObservers = [
          {
            event: 'mc_roomplayerschange',
            callback: (data) => {
              this.room.players = data[2];
            },
          },
        ];
      }

      getReadyState() {
        return Boolean(this.socket && this.socket.readyState === WebSocket.OPEN);
      }

      getConnectingState() {
        return Boolean(this.socket && this.socket.readyState === WebSocket.CONNECTING);
      }

      connect(inviteOrRoomId = this.room.id) {
        if (this.getReadyState() || this.getConnectingState()) return false;

        const target = String(inviteOrRoomId ?? '').trim();
        const explicitServer = /^\d+$/.test(target) ? target : null;
        const roomId = explicitServer ? this.room.id : parseRoomId(target) || this.room.id;
        if (!roomId) {
          console.warn('Cubic Engine: Bot connection requires a valid Drawaria room invite.');
          return false;
        }

        this.room.id = roomId;
        this.url = parseServerUrl(explicitServer || roomId);

        const socket = new WebSocket(this.url);
        this.socket = socket;

        socket.addEventListener('open', () => {
          if (this.socket !== socket) return;
          this.#startHeartbeat();
          // this.send('40');
        });

        socket.addEventListener('message', (messageEvent) => {
          if (this.socket !== socket) return;
          this.#handleMessage(messageEvent);
        });

        socket.addEventListener(
          'close',
          () => {
            if (this.socket !== socket) return;
            this.#stopHeartbeat();
            this.socket = null;
            this.attributes.spawned = false;
          },
          { once: true }
        );

        socket.addEventListener('error', () => {
          if (this.socket === socket) console.warn(`Cubic Engine: Bot socket failed to connect to ${this.url}.`);
        });

        return true;
      }

      disconnect(sendLeave = true) {
        const socket = this.socket;
        if (!socket) return false;

        this.#stopHeartbeat();
        if (sendLeave && socket.readyState === WebSocket.OPEN) socket.send('41');
        if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) socket.close();
        if (this.socket === socket) this.socket = null;
        this.attributes.spawned = false;
        return true;
      }

      reconnect() {
        if (!this.room.id) return false;
        if (!this.getReadyState()) {
          return this.getConnectingState() ? false : this.connect(this.room.id);
        }

        this.send('41');
        this.send('40');
        return true;
      }

      enterRoom(roomid) {
        const inviteValue = roomid ?? document.querySelector('#invurl')?.value;
        const nextRoomId = parseRoomId(inviteValue);
        if (!nextRoomId) {
          console.warn('Cubic Engine: Invalid Drawaria room invite.');
          return false;
        }

        const currentServerUrl = this.url;
        const nextServerUrl = parseServerUrl(nextRoomId);
        this.room.id = nextRoomId;

        if (currentServerUrl === nextServerUrl) {
          if (this.getReadyState()) return this.reconnect();
          if (this.getConnectingState()) return true;
        }

        if (this.socket) this.disconnect(false);
        return this.connect(nextRoomId);
      }

      leaveRoom() {
        this.attributes.spawned = false;
        return this.send('41');
      }

      switchRoom() {
        this.emit('pgswtichroom');
        // this.send(emits['pgswtichroom']());
      }

      addEventListener(eventname, callback) {
        if (typeof callback !== 'function') return function () {};
        const observer = { event: eventname, callback };
        this.customObservers.push(observer);
        return () => {
          const index = this.customObservers.indexOf(observer);
          if (index !== -1) this.customObservers.splice(index, 1);
        };
      }

      send(data) {
        if (!this.getReadyState()) return false;
        this.socket.send(data);
        return true;
      }

      emit(event, ...data) {
        // data = data.length > 0 ? data : null;
        const emitter = emits[event];
        return emitter ? this.send(emitter(...data)) : false;
      }

      #startHeartbeat() {
        this.#stopHeartbeat();
        this.interval_id = setInterval(() => {
          if (!this.getReadyState()) return this.#stopHeartbeat();
          this.send('2');
        }, this.interval_ms);
      }

      #stopHeartbeat() {
        if (!this.interval_id) return;
        clearInterval(this.interval_id);
        this.interval_id = 0;
      }

      #handleMessage(messageEvent) {
        const rawData = String(messageEvent.data ?? '');
        const prefixMatch = rawData.match(/^\d+/);
        if (!prefixMatch) return;

        const prefix = prefixMatch[0];
        if (prefix === '40') {
          this.send(emits.startplay(this.room, this.name, this.avatar));
        }

        const data = parseSocketIOEvent(prefix.length, rawData);
        if (!Array.isArray(data)) return;

        if (data.length === 1 && data[0]?.players) {
          this.room.players = data[0].players;
          return;
        }

        if (data.length <= 1) return;
        const [event, ...eventData] = data;
        this.customObservers.forEach((listener) => {
          if (listener.event !== event) return;
          try {
            listener.callback(eventData);
          } catch (error) {
            console.error(`Cubic Engine: Bot listener for ${event} failed.`, error);
          }
        });
      }
    }

    const emits = {
      chatmsg: function (message) {
        // 42["chatmsg","a"]
        let data = ['chatmsg', message];
        return `${42}${JSON.stringify(data)}`;
      },
      passturn: function () {
        // 42["passturn"]
        let data = ['passturn'];
        return `${42}${JSON.stringify(data)}`;
      },
      pgdrawvote: function (playerid) {
        // 42["pgdrawvote",2,0]
        let data = ['pgdrawvote', playerid, 0];
        return `${42}${JSON.stringify(data)}`;
      },
      pgswtichroom: function () {
        // 42["pgswtichroom"]
        let data = ['pgswtichroom'];
        return `${42}${JSON.stringify(data)}`;
      },
      playerafk: function () {
        // 42["playerafk"]
        let data = ['playerafk'];
        return `${42}${JSON.stringify(data)}`;
      },
      playerrated: function () {
        // 42["playerrated"]
        let data = ['playerrated'];
        return `${42}${JSON.stringify(data)}`;
      },
      sendgesture: function (gestureid) {
        // 42["sendgesture",16]
        let data = ['sendgesture', gestureid];
        return `${42}${JSON.stringify(data)}`;
      },
      sendvote: function () {
        // 42["sendvote"]
        let data = ['sendvote'];
        return `${42}${JSON.stringify(data)}`;
      },
      sendvotekick: function (playerid) {
        // 42["sendvotekick",93]
        let data = ['sendvotekick', playerid];
        return `${42}${JSON.stringify(data)}`;
      },
      wordselected: function (wordid) {
        // 42["wordselected",0]
        let data = ['wordselected', wordid];
        return `${42}${JSON.stringify(data)}`;
      },
      activateitem: function (itemid, isactive) {
        let data = ['clientcmd', 12, [itemid, isactive]];
        return `${42}${JSON.stringify(data)}`;
      },
      buyitem: function (itemid) {
        let data = ['clientcmd', 11, [itemid]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_changeattr: function (itemid, target, value) {
        // target = zindex || shared
        let data = ['clientcmd', 234, [itemid, target, value]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_getobjects: function () {
        let data = ['clientcmd', 233];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_remove: function (itemid) {
        let data = ['clientcmd', 232, [itemid]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_setposition: function (itemid, positionX, positionY, speed) {
        let data = ['clientcmd', 230, [itemid, 100 / positionX, 100 / positionY, { movespeed: speed }]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_setrotation: function (itemid, rotation) {
        let data = ['clientcmd', 231, [itemid, rotation]];
        return `${42}${JSON.stringify(data)}`;
      },
      customvoting_setvote: function (value) {
        let data = ['clientcmd', 301, [value]];
        return `${42}${JSON.stringify(data)}`;
      },
      getfpid: function (value) {
        let data = ['clientcmd', 901, [value]];
        return `${42}${JSON.stringify(data)}`;
      },
      getinventory: function () {
        let data = ['clientcmd', 10, [true]];
        return `${42}${JSON.stringify(data)}`;
      },
      getspawnsstate: function () {
        let data = ['clientcmd', 102];
        return `${42}${JSON.stringify(data)}`;
      },
      moveavatar: function (positionX, positionY) {
        let data = [
          'clientcmd',
          103,
          [1e4 * Math.floor((positionX / 100) * 1e4) + Math.floor((positionY / 100) * 1e4), false],
        ];
        return `${42}${JSON.stringify(data)}`;
      },
      setavatarprop: function () {
        let data = ['clientcmd', 115];
        return `${42}${JSON.stringify(data)}`;
      },
      setstatusflag: function (flagid, isactive) {
        let data = ['clientcmd', 3, [flagid, isactive]];
        return `${42}${JSON.stringify(data)}`;
      },
      settoken: function (playerid, tokenid) {
        let data = ['clientcmd', 2, [playerid, tokenid]];
        return `${42}${JSON.stringify(data)}`;
      },
      snapchatmessage: function (playerid, value) {
        let data = ['clientcmd', 330, [playerid, value]];
        return `${42}${JSON.stringify(data)}`;
      },
      spawnavatar: function () {
        let data = ['clientcmd', 101];
        return `${42}${JSON.stringify(data)}`;
      },
      startrollbackvoting: function () {
        let data = ['clientcmd', 320];
        return `${42}${JSON.stringify(data)}`;
      },
      trackforwardvoting: function () {
        let data = ['clientcmd', 321];
        return `${42}${JSON.stringify(data)}`;
      },
      startplay: function (room, name, avatar) {
        let data = `${420}${JSON.stringify(['startplay', name, room.type, 'en', room.id, null, [null, 'https://drawaria.online/', 1000, 1000, [null, avatar[0], avatar[1]], null]])}`;
        return data;
      },
      votetrack: function (trackid) {
        let data = ['clientcmd', 1, [trackid]];
        return `${42}${JSON.stringify(data)}`;
      },
      requestcanvas: function (playerid) {
        let data = ['clientnotify', playerid, 10001];
        return `${42}${JSON.stringify(data)}`;
      },
      respondcanvas: function (playerid, base64) {
        let data = ['clientnotify', playerid, 10002, [base64]];
        return `${42}${JSON.stringify(data)}`;
      },
      galleryupload: function (playerid, imageid) {
        let data = ['clientnotify', playerid, 11, [imageid]];
        return `${42}${JSON.stringify(data)}`;
      },
      warning: function (playerid, type) {
        let data = ['clientnotify', playerid, 100, [type]];
        return `${42}${JSON.stringify(data)}`;
      },
      mute: function (playerid, targetname, mute = 0) {
        let data = ['clientnotify', playerid, 1, [mute, targetname]];
        return `${42}${JSON.stringify(data)}`;
      },
      hide: function (playerid, targetname, hide = 0) {
        let data = ['clientnotify', playerid, 3, [hide, targetname]];
        return `${42}${JSON.stringify(data)}`;
      },
      report: function (playerid, reason, targetname) {
        let data = ['clientnotify', playerid, 2, [targetname, reason]];
        return `${42}${JSON.stringify(data)}`;
      },
      line: function (playerid, lastx, lasty, x, y, isactive, size, color, ispixel) {
        let data = [
          'drawcmd',
          0,
          [lastx / 100, lasty / 100, x / 100, y / 100, isactive, -size, color, playerid, ispixel],
        ];
        return `${42}${JSON.stringify(data)}`;
      },
      erase: function (playerid, lastx, lasty, x, y, isactive, size, color) {
        let data = ['drawcmd', 1, [lastx / 100, lasty / 100, x / 100, y / 100, isactive, -size, color, playerid]];
        return `${42}${JSON.stringify(data)}`;
      },
      flood: function (x, y, color, size, r, g, b, a) {
        // 42["drawcmd",2,[x, y,color,{"0":r,"1":g,"2":b,"3":a},size]]
        let data = ['drawcmd', 2, [x / 100, y / 100, color, { 0: r, 1: g, 2: b, 3: a }, size]];
        return `${42}${JSON.stringify(data)}`;
      },
      undo: function (playerid) {
        // 42["drawcmd",3,[playerid]]
        let data = ['drawcmd', 3, [playerid]];
        return `${42}${JSON.stringify(data)}`;
      },
      clear: function () {
        // 42["drawcmd",4,[]]
        let data = ['drawcmd', 4, []];
        return `${42}${JSON.stringify(data)}`;
      },
      noop: function () {
        // 42["drawcmd",5,[0.44882022129015975,0.3157894736842105,0.44882022129015975,0.3157894736842105,true,-12,"#000000",playerid]]
      },
    };
    const events = {
      bc_announcement: function (data) {
        //
      },
      bc_chatmessage: function (data) {
        // 42["bc_chatmessage",3,"playername","a"]
      },
      bc_clearcanvasobj: function (data) {
        //
      },
      bc_clientnotify: function (data) {
        // 42["bc_clientnotify",playerid,"playername",code,null]
      },
      bc_createcanvasobj: function (data) {
        // 42["bc_createcanvasobj","1",[3,63001,0.5,0.5,0,1,null,"1",true]]
      },
      bc_customvoting_abort: function (data) {
        //
      },
      bc_customvoting_error: function (data) {
        // 42["bc_customvoting_error","rollbackcanvas"]
      },
      bc_customvoting_results: function (data) {
        // 42["bc_customvoting_results",[2],true,0]
      },
      bc_customvoting_start: function (data) {
        // 42["bc_customvoting_start",{"type":321,"secs":20,"acceptratios":[0.51],"pgdrawallow":true,"voteoptions":["YES","NO"]},1]
      },
      bc_customvoting_vote: function (data) {
        // 42["bc_customvoting_vote",1,0,[2,1,[1]]]
      },
      bc_exp: function (data) {
        // 42["bc_exp",29,4357]
      },
      bc_extannouncement: function (data) {
        //
      },
      bc_freedrawsession_reset: function (data) {
        // 42["bc_freedrawsession_reset",-1,{"votingtype":2,"currentvotes":0,"neededvotes":2,"votingtimeout":null}null]
      },
      bc_gesture: function (data) {
        // 42["bc_gesture",3,31]
      },
      bc_musicbox_play: function (data) {
        // 42["bc_musicbox_play",[30394,1,"37678185",252386,1661295694733,"Sony Masterworks - Smooth Criminal","2cellos/smooth-criminal"]]
      },
      bc_musicbox_vote: function (data) {
        // 42["bc_musicbox_vote",[[30394,1]],3,30394]
      },
      bc_pgdrawallow_results: function (data) {
        // 42["bc_pgdrawallow_results",2,true,true]
      },
      bc_pgdrawallow_startvoting: function (data) {
        // 42["bc_pgdrawallow_startvoting",2,1,false]
      },
      bc_pgdrawallow_vote: function (data) {
        // 42["bc_pgdrawallow_vote",2,1,0,false,[1,0]]
      },
      bc_playerafk: function (data) {
        // 42["bc_playerafk",28,"Jinx"]
      },
      bc_playerrated: function (data) {
        // 42["bc_playerrated",1,29,"lil cute girl",28,"Jinx",[1]]
      },
      bc_removecanvasobj: function (data) {
        // 42["bc_removecanvasobj",3,"1",null]
      },
      bc_resetplayername: function (data) {
        //
      },
      bc_round_results: function (data) {
        // 42["bc_round_results",[[5,"Jinx",15,61937,3,"63196790-c7da-11ec-8266-c399f90709b7",0],[4,"ツ♡thick mojo ♡ツ",15,65464,3,"018cdc20-47a4-11ec-b5b5-6bdacecdd51e",1]]]
      },
      bc_setavatarprop: function (data) {
        // 42["bc_setavatarprop",3]
      },
      bc_setobjattr: function (data) {
        // 42["bc_setobjattr","1","shared",false]
      },
      bc_setstatusflag: function (data) {
        // 42["bc_setstatusflag",3,3,true]
      },
      bc_spawnavatar: function (data) {
        // 42["bc_spawnavatar",3,true]
      },
      bc_startinground: function (data) {
        // 42["bc_startinground",200000,[],{"votingtype":0,"currentvotes":0,"neededvotes":2,"votingtimeout":null}]
      },
      bc_token: function (data) {
        // 42["bc_token",1,3,0]
      },
      bc_turn_abort: function (data) {
        // 42["bc_turn_abort","pass","lil cute girl","2c276aa0-dc5e-11ec-9fd3-c3a00b129da4","hammer",null]
      },
      bc_turn_fastout: function (data) {
        // 42["bc_turn_fastout",15000]
      },
      bc_turn_results: function (data) {
        // 42["bc_turn_results",[[1,"Jinx",2,2,"63196790-c7da-11ec-8266-c399f90709b7",0,0],[2,"vale",3,3,"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.xxxxxxxxxxxxx",9248]],"cavern"]
      },
      bc_turn_waitplayers: function (data) {
        // 42["bc_turn_waitplayers",true,-1,6]
      },
      bc_uc_freedrawsession_changedroom: function (data) {
        // console.log(data[2], data[3])
        // 42["bc_uc_freedrawsession_changedroom",[list of drawlines not !important]]
      },
      bc_uc_freedrawsession_start: function (data) {
        //
      },
      bc_votekick: function (data) {
        // 42["bc_votekick","Jinx",22,true]
      },
      bc_votingtimeout: function (data) {
        // 42["bc_votingtimeout",{"votingtype":2,"currentvotes":0,"neededvotes":2,"votingtimeout":null}]
      },
      bcmc_playervote: function (data) {
        // 42["bcmc_playervote","playername",{"votingtype":3,"currentvotes":1,"neededvotes":2,"votingtimeout":1661296731309}]
      },
      bcuc_getfpid: function (data) {
        // 42["bcuc_getfpid"]
        // 42["clientcmd",901,[{"visitorId":"a8923f0870050d4a4e771cd26679ab6e"}]]
      },
      bcuc_itemactivated: function (data) {
        // 42["bcuc_itemactivated",3,63001,[2,[0.5,0.5],0,1,null],1]
      },
      bcuc_itemactivationabort: function (data) {
        //
      },
      bcuc_moderatormsg: function (data) {
        // 42["bcuc_moderatormsg","Kick Player",true]
      },
      bcuc_snapchatmessage: function (data) {
        // 42["uc_snapshot","1671740010120.1.28028"]
        // https://drawaria.online/snapshot/save
      },
      mc_drawcommand: function (data) {
        // 42["mc_drawcommand",0,[0.2958167330677291,0.24970131421744324,0.2958167330677291,0.24970131421744324,true,-12,"#000000",3]]
      },
      mc_moveavatar: function (data) {
        // 42["mc_moveavatar",3,36081456,true]
      },
      mc_moveobj: function (data) {
        // 42["mc_moveobj","1",0.8266237186146181,0.24248391556470414,3,{"movespeed":500}]
      },
      mc_roomplayerschange: function (data) {
        // console.log(data[2])
        // 42["mc_roomplayerschange","join","playername",[{"id":1,"name":"ᴮᴱᴺᴵᴹᴬᴿᵁ","turnscore":0,"roundscore":0,"roundguesstime":0,"avatarid":"81253f20-ff93-11ec-9fd3-c3a00b129da4.1661276848726","account_stats":null,"from":"TR","medals":0,"turnstarcount":0,"statusflags":[]}{"id":3,"name":"playername","turnscore":0,"roundscore":0,"roundguesstime":0,"avatarid":"81253f20-ff93-11ec-9fd3-c3a00b129da4.1661276848726","account_stats":null,"from":"GB","medals":0,"turnstarcount":0,"statusflags":[]}],{"votingtype":2,"currentvotes":0,"neededvotes":0,"votingtimeout":null}false,3]
      },
      mc_rotateobj: function (data) {
        // 42["mc_rotateobj","1",0.2617993877991494,3]
      },
      mc_turn_guessdraw: function (data) {
        // 42["mc_turn_guessdraw",90000,[],"Ǝ̵͍͖͓͒͑͊E̵̠̼̔͘͜͝",{"votingtype":1,"currentvotes":0,"neededvotes":2,"votingtimeout":null}false]
      },
      mc_turn_tip: function (data) {
        // 42["mc_turn_tip","_a_m__"]
      },
      mc_turn_waitselectword: function (data) {
        // 42["mc_turn_waitselectword",11000,"Ǝ̵͍͖͓͒͑͊E̵̠̼̔͘͜͝",6,"c46de8f0-f493-11ec-9fd3-c3a00b129da4",2,5,false]
      },
      mc_turn_wordguessed: function (data) {
        // 42["mc_turn_wordguessed","vale",[[2,3,3,9248],[1,2,2,0]]]
      },
      uc_avatarspawninfo: function (data) {
        // 42["uc_avatarspawninfo","9a2ab5b2-b81e-4690-9af7-475d870d6e20",[[38,75059625,0]]]
      },
      uc_buyitemerror: function (data) {
        //
      },
      uc_canvasobjs: function (data) {
        // 42["uc_canvasobjs","9a2ab5b2-b81e-4690-9af7-475d870d6e20",{}]
      },
      uc_chatmuted: function (data) {
        // 42["uc_chatmuted"]
      },
      uc_coins: function (data) {
        // 42["uc_coins",-50,43]
      },
      uc_inventoryitems: function (data) {
        // 42["uc_inventoryitems",[[100,99,null],[63000,null,null],[86000,null,null]],false,false] list
      },
      uc_resetavatar: function (data) {
        //
      },
      uc_serverserstart: function (data) {
        //
      },
      uc_snapshot: function (data) {
        //
      },
      uc_tokenerror: function (data) {
        // 42["uc_tokenerror",2]
      },
      uc_turn_begindraw: function (data) {
        // 42["uc_turn_begindraw",90000,"arrow"]
      },
      uc_turn_selectword: function (data) {
        // 42["uc_turn_selectword",11000,["vase","cellar","rain"],1,7,false]
      },
      uc_turn_selectword_refreshlist: function (data) {
        // 42["uc_turn_selectword_refreshlist",["crayons","trade","team"]]
      },
      uc_turn_wordguessedlocalThis: function (data) {
        // 42["uc_turn_wordguessedlocalThis","stage",3,[[2,3,3,53938],[1,2,2,0]]]
      },
    };

    globalThis['_io'] = { events, emits };
  })('QBit');

  (function BiggerBrush() {
    const QBit = globalThis[arguments[0]];

    class BiggerBrush extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      active;
      constructor() {
        super('BiggerBrush', '<i class="fas fa-brush"></i>');
        this.active = false;
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
        this.drawwidthrangeSlider = document.querySelector('#drawwidthrange');
        // this.enable();
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          const enableButton = domMake.Button('Enable');

          enableButton.addEventListener('click', (event) => {
            this.active ? this.disable() : this.enable();
          });
          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      enable() {
        if (!this.drawwidthrangeSlider) return this.notify('warning', 'Brush controls are unavailable.');
        document.querySelectorAll('.drawcontrols-button').forEach((n) => {
          n.classList.remove('drawcontrols-disabled');
        });
        this.active = true;

        this.htmlElements.toggleStatusButton.classList.add('active');
        this.htmlElements.toggleStatusButton.textContent = 'Active';

        this.drawwidthrangeSlider.parentElement.previousElementSibling.lastElementChild.click();
        this.drawwidthrangeSlider.parentElement.style.display = 'flex';
        this.drawwidthrangeSlider.max = 48;
        this.drawwidthrangeSlider.min = -2000;

        this.notify('success', `enabled`);
      }

      disable() {
        this.active = false;

        this.htmlElements.toggleStatusButton.classList.remove('active');
        this.htmlElements.toggleStatusButton.textContent = 'Inactive';

        this.drawwidthrangeSlider.max = 45;
        this.drawwidthrangeSlider.min = -100;

        this.notify('warning', `disabled`);
      }
    }
  })('QBit');

  (function BetterBrush() {
    const QBit = globalThis[arguments[0]];

    class BetterBrush extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      constructor() {
        super('BetterBrush', '<i class="fas fa-magic"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();

        {
          const target = document.querySelector('.drawcontrols-popuplist');
          if (!target) return;

          const visibilityOvserver = new MutationObserver((mutations) => {
            if (this.active)
              if (mutations[0].target.style.display !== 'none') {
                mutations[0].target.querySelectorAll('div').forEach((n) => {
                  n.removeAttribute('style');
                });
              }
          });

          visibilityOvserver.observe(target, {
            attributes: true,
          });
        }
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          const enableButton = domMake.Button('Enable');

          enableButton.addEventListener('click', (event) => {
            this.active ? this.disable() : this.enable();
          });

          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      enable() {
        this.active = true;

        this.htmlElements.toggleStatusButton.classList.add('active');
        this.htmlElements.toggleStatusButton.textContent = 'Active';

        this.notify('success', `enabled`);
      }

      disable() {
        this.active = false;

        this.htmlElements.toggleStatusButton.classList.remove('active');
        this.htmlElements.toggleStatusButton.textContent = 'Inactive';

        this.notify('warning', `disabled`);
      }
    }
  })('QBit');

  (function BiggerStencil() {
    const QBit = globalThis[arguments[0]];

    class BiggerStencil extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      active;

      constructor() {
        super('BiggerStencil', '<i class="fas fa-parachute-box"></i>');
        this.active = false;
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();

        {
          const target = document.querySelector('.fa-parachute-box')?.parentElement;
          if (!target) return;
          const accessabilityObserver = new MutationObserver((mutations) => {
            if (this.active)
              if (mutations[0].target.disabled) {
                mutations[0].target.disabled = false;
              }
          });

          accessabilityObserver.observe(target, {
            attributes: true,
          });
        }
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          const enableButton = domMake.Button('Enable');

          enableButton.addEventListener('click', (event) => {
            this.active ? this.disable() : this.enable();
          });
          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      enable() {
        this.active = true;

        this.htmlElements.toggleStatusButton.classList.add('active');
        this.htmlElements.toggleStatusButton.textContent = 'Active';

        this.notify('success', `enabled`);
      }

      disable() {
        this.active = false;

        this.htmlElements.toggleStatusButton.classList.remove('active');
        this.htmlElements.toggleStatusButton.textContent = 'Inactive';

        this.notify('warning', `disabled`);
      }
    }
  })('QBit');

  (function BotClientInterface() {
    const QBit = globalThis[arguments[0]];
    const BotClient = QBit.findGlobal('BotClient');

    let botcount = 0;
    const radios = [];

    function getMasterId() {
      return document.querySelector('.playerlist-name-self')?.parentElement?.dataset?.playerid || 0;
    }

    function parseAvatarURL(arr = []) {
      return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join('.') : 'default'}.jpg`;
    }

    class BotClientManager extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      constructor() {
        super(`BotClientManager`, '<i class="fas fa-robot"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.IconList();
        {
          const id = generate.uuidv4();
          const createBotClientInterfaceInput = domMake.Tree('input', {
            type: 'button',
            id: id,
            hidden: true,
          });
          const createBotClientInterfaceLabel = domMake.Tree('label', { for: id, class: 'icon', title: 'Add Bot' }, [
            domMake.Tree('i', { class: 'fas fa-plus' }),
          ]);

          createBotClientInterfaceInput.addEventListener('click', () => {
            this.createBotClientInterface();
          });

          row.appendAll(createBotClientInterfaceLabel);
          row.appendAll(createBotClientInterfaceInput);
        }
        {
          const id = generate.uuidv4();
          const removeBotClientInterfaceInput = domMake.Tree('input', {
            type: 'button',
            id: id,
            hidden: true,
          });
          const removeBotClientInterfaceLabel = domMake.Tree('label', { for: id, class: 'icon', title: 'Remove Bot' }, [
            domMake.Tree('i', { class: 'fas fa-minus' }),
          ]);

          removeBotClientInterfaceInput.addEventListener('click', () => {
            this.deleteBotClientInterface();
          });

          row.appendAll(removeBotClientInterfaceLabel);
          row.appendAll(removeBotClientInterfaceInput);
        }
        this.htmlElements.header.before(row);
      }

      createBotClientInterface() {
        const instance = this.loadExtension(BotClientInterface);
        instance.htmlElements.input.type = 'radio';
        instance.htmlElements.input.name = 'botClient';

        radios.push(instance.htmlElements.input);
        instance.htmlElements.input.addEventListener('change', (event) => {
          radios.forEach(function (radio) {
            document.body.querySelector(`label[for="${radio.id}"]`).classList.remove('active');
          });
          instance.htmlElements.label.classList.add('active');
        });

        return instance;
      }

      deleteBotClientInterface() {
        const input = document.body.querySelector(`input[name="botClient"]:checked`);

        const matches = this.children.filter((child) => {
          return child.htmlElements.input === input;
        });
        if (matches.length <= 0) return;

        const instance = matches[0];

        instance.bot.disconnect();

        const labelPos = radios.indexOf(instance.htmlElements.input);
        if (~labelPos) radios.splice(labelPos, 1);

        instance._EXP_destroy(!0);
      }
    }

    class BotClientInterface extends QBit {
      static dummy1 = QBit.register(this);
      // static dummy2 = QBit.bind(this, 'CubeEngine');
      // static dummy3 = QBit.bind(this, 'CubeEngine');

      constructor() {
        super(`Bot${++botcount}`, '<i class="fas fa-robot"></i>');
        this.bot = new BotClient();
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
        this.setClientName(this.bot.name);
        this.setClientIcon(this.bot.avatar);
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          let join_button = domMake.Button('Enter');
          let leave_button = domMake.Button('Leave');

          join_button.addEventListener('click', (event) => {
            this.bot.enterRoom(document.querySelector('#invurl')?.value);
          });

          leave_button.addEventListener('click', (event) => {
            this.bot.disconnect();
          });

          row.appendAll(join_button, leave_button);
        }
        this.htmlElements.section.appendChild(row);
      }

      setClientName(name) {
        this.setName(name);
        this.bot.name = name;
      }

      setClientIcon(icon) {
        this.bot.avatar = icon;
        this.setIcon(`<img src="${parseAvatarURL(icon)}">`);
      }
    }
  })('QBit');

  (function BotClientInteractions() {
    const QBit = globalThis[arguments[0]];

    class BotPersonality extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'BotClientInterface');

      constructor() {
        super('Personality', '<i class="fas fa-user-cog"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          let botName = domMake.Tree('input', { type: 'text', placeholder: 'Your Bots name' });
          let botNameAccept = domMake.Tree('button', { class: 'icon' }, [domMake.Tree('i', { class: 'fas fa-check' })]);

          botNameAccept.addEventListener('click', (event) => {
            this.parent.setClientName(botName.value);
          });

          row.appendAll(botName, botNameAccept);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        let id = generate.uuidv4();
        const row = domMake.Row();
        {
          let botAvatarUpload = domMake.Tree('input', { type: 'file', id: id, hidden: true });
          let botAvatarAccept = domMake.Tree('label', { for: id, class: 'btn btn-outline-secondary' }, [
            'Upload BotAvatar',
          ]);

          const localThis = this;
          function onChange() {
            if (!this.files || !this.files[0]) return;
            let myFileReader = new FileReader();
            myFileReader.addEventListener('load', (e) => {
              let a = e.target.result.replace('image/gif', 'image/png');
              fetch('https://drawaria.online/uploadavatarimage', {
                method: 'POST',
                body: 'imagedata=' + encodeURIComponent(a) + '&fromeditor=true',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
              }).then((res) =>
                res.text().then((body) => {
                  localThis.parent.setClientIcon(body.split('.'));
                })
              );
            });
            myFileReader.readAsDataURL(this.files[0]);
          }
          botAvatarUpload.addEventListener('change', onChange);

          row.appendAll(botAvatarUpload, botAvatarAccept);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotSozials extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'BotClientInterface');

      constructor() {
        super('Socialize', '<i class="fas fa-comment-dots"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          let messageClear_button = domMake.Button('<i class="fas fa-strikethrough"></i>');
          let messageSend_button = domMake.Button('<i class="fas fa-paper-plane"></i>');
          let message_input = domMake.Tree('input', { type: 'text', placeholder: 'message...' });

          messageClear_button.classList.add('icon');
          messageSend_button.classList.add('icon');

          messageClear_button.addEventListener('click', (event) => {
            message_input.value = '';
          });

          messageSend_button.addEventListener('click', (event) => {
            this.parent.bot.emit('chatmsg', message_input.value);
          });

          message_input.addEventListener('keydown', (event) => {
            if (event.key !== 'Enter') return;
            this.parent.bot.emit('chatmsg', message_input.value);
          });

          row.appendAll(messageClear_button, message_input, messageSend_button);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.IconList();
        // row.classList.add('nowrap');
        {
          document
            .querySelectorAll('#gesturespickerselector .gesturespicker-container .gesturespicker-item')
            .forEach((node, index) => {
              let clone = node.cloneNode(true);
              clone.classList.add('icon');
              clone.addEventListener('click', (event) => {
                this.parent.bot.emit('sendgesture', index);
              });
              row.appendChild(clone);
            });
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotTokenGiver extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'BotClientInterface');

      constructor() {
        super('Tokken', '<i class="fas fa-thumbs-up"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.IconList();
        // row.classList.add('nowrap');
        {
          let listOfTokens = [
            '<i class="fas fa-thumbs-up"></i>',
            '<i class="fas fa-heart"></i>',
            '<i class="fas fa-paint-brush"></i>',
            '<i class="fas fa-cocktail"></i>',
            '<i class="fas fa-hand-peace"></i>',
            '<i class="fas fa-feather-alt"></i>',
            '<i class="fas fa-trophy"></i>',
            '<i class="fas fa-mug-hot"></i>',
            '<i class="fas fa-gift"></i>',
          ];
          listOfTokens.forEach((token, index) => {
            let tokenSend_button = domMake.Button(token);
            tokenSend_button.classList.add('icon');
            tokenSend_button.addEventListener('click', () => {
              this.parent.bot.room.players.forEach((player) => {
                this.parent.bot.emit('settoken', player.id, index);
              });
            });
            row.appendChild(tokenSend_button);
          });
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          let toggleStatus_button = domMake.Button('Toggle Status');
          toggleStatus_button.addEventListener('click', () => {
            this.parent.bot.attributes.status = !this.parent.bot.attributes.status;
            let status = this.parent.bot.attributes.status;
            toggleStatus_button.classList[status ? 'add' : 'remove']('active');
            this.parent.bot.emit('setstatusflag', 0, status);
            this.parent.bot.emit('setstatusflag', 1, status);
            this.parent.bot.emit('setstatusflag', 2, status);
            this.parent.bot.emit('setstatusflag', 3, status);
            this.parent.bot.emit('setstatusflag', 4, status);
          });
          row.appendChild(toggleStatus_button);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotCanvasAvatar extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'BotClientInterface');

      constructor() {
        super('SpawnAvatar', '<i class="fas fa-user-circle"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          // Spawn && Move Avatar
          let avatarPosition = { x: 0, y: 0 };

          let avatarSpawn_button = domMake.Button('<i class="fas fa-exchange-alt"></i>');
          let avatarChange_button = domMake.Button('<i class="fas fa-retweet"></i>');
          let avatarPositionX_button = domMake.Tree('input', {
            type: 'number',
            value: 2,
            min: 2,
            max: 98,
            title: 'Left',
          });
          let avatarPositionY_button = domMake.Tree('input', {
            type: 'number',
            value: 2,
            min: 2,
            max: 98,
            title: 'Top',
          });

          avatarSpawn_button.addEventListener('click', (event) => {
            this.parent.bot.emit('spawnavatar');
            this.parent.bot.attributes.spawned = !this.parent.bot.attributes.spawned;
          });

          avatarChange_button.addEventListener('click', (event) => {
            this.parent.bot.emit('setavatarprop');
            this.parent.bot.attributes.rounded = !this.parent.bot.attributes.rounded;
          });

          avatarPositionX_button.addEventListener('change', (event) => {
            avatarPosition.x = avatarPositionX_button.value;
            this.parent.bot.emit('moveavatar', avatarPosition.x, avatarPosition.y);
          });

          avatarPositionY_button.addEventListener('change', (event) => {
            avatarPosition.y = avatarPositionY_button.value;
            this.parent.bot.emit('moveavatar', avatarPosition.x, avatarPosition.y);
          });

          avatarSpawn_button.title = 'Spawn Avatar';
          avatarChange_button.title = 'Toggle Round Avatar';

          row.appendAll(avatarSpawn_button, avatarPositionX_button, avatarPositionY_button, avatarChange_button);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    function getMyId() {
      return document.querySelector('.playerlist-name-this')?.parentElement?.dataset?.playerid || 0;
    }

    function parseAvatarURL(arr = []) {
      return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join('.') : 'default'}.jpg`;
    }
  })('QBit');

  (function AutoDraw() {
    const QBit = globalThis[arguments[0]];

    class AutoDraw extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      constructor() {
        super('AutoDraw', '<i class="fas fa-brush"></i>');
        this.executionList = [];
        this.isRunning = false;
        this.taskTimer = 0;
        this.botManager = this.findGlobal('BotClientManager')?.siblings?.at(0);
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
        this.#row3();
        this.#row4();
      }

      #row1() {}
      #row2() {}
      #row3() {}
      #row4() {}

      startDrawing() {
        this.botManager = this.findGlobal('BotClientManager')?.siblings?.at(0);
        if (this.isRunning) return;
        if (this.executionList.length === 0) return this.notify('warning', 'No pixels are loaded.');
        if (!this.#getConnectedBots().length) return this.notify('warning', 'No connected bot is available.');

        this.isRunning = true;
        this.doTasks();
        this.notify('info', 'Started');
      }

      stopDrawing(notify = false) {
        this.isRunning = false;
        clearTimeout(this.taskTimer);
        this.taskTimer = 0;
        if (notify) this.notify('info', 'Stopped');
      }

      doTasks() {
        if (!this.isRunning) return;

        const bots = this.#getConnectedBots();
        if (!bots.length || !this.executionList.length) return this.stopDrawing(true);

        bots.forEach((bot) => {
          const pixelInstance = this.executionList.shift();
          if (!pixelInstance) return;
          const data = [
            'drawcmd',
            0,
            [
              pixelInstance.x1 * 0.01,
              pixelInstance.y1 * 0.01,
              pixelInstance.x2 * 0.01,
              pixelInstance.y2 * 0.01,
              !0,
              -pixelInstance.thickness,
              pixelInstance.colorAsHexFull,
              -1,
              !1,
            ],
          ];
          bot.send(`${42}${JSON.stringify(data)}`);
        });

        const ghostCanvas = this.findLocal('GhostCanvas')[0];
        if (ghostCanvas?.htmlElements?.pixelsLeftToDraw) {
          ghostCanvas.htmlElements.pixelsLeftToDraw.value = this.executionList.length;
        }

        if (!this.executionList.length) return this.stopDrawing(true);
        this.taskTimer = setTimeout(() => {
          this.doTasks();
        }, 5);
      }

      #getConnectedBots() {
        if (!this.botManager?.children) return [];
        return this.botManager.children.map((botInterface) => botInterface.bot).filter((bot) => bot?.getReadyState());
      }
    }

    class Pixel {
      static dummy1 = QBit.register(this);

      constructor(x = 0, y = 0, color = [0, 0, 0, 0]) {
        this.x1 = x;
        this.x2 = x;
        this.y1 = y;
        this.y2 = y;
        this.thickness = 2;
        this.color = color;
      }

      get colorAsHexFull() {
        return rgbaArrayToHex(this.color);
      }

      get colorAsHexShort() {
        const hexString = rgbaArrayToHex(this.color);
        const matches = hexString.match(/^(#)(.).(.).(.).(.).$/);
        return matches[1] + matches[2] + matches[3] + matches[4] + matches[5];
      }

      get colorAsHexShortWild() {
        const hexString = rgbaArrayToHex(this.color);
        const matches = hexString.match(/^(#).(.).(.).(.)(.).$/);
        return matches[1] + matches[2] + matches[3] + matches[4] + matches[5];
      }
    }

    /**
     *
     * @param {0} number - A number from 0-255 to assign to hex values
     * @returns The passed number as hex string
     */
    function intToHex(number) {
      return number.toString(16).padStart(2, '0');
    }
    /**
     *
     * @param {[0,0,0]|[0,0,0,0]} rgbaArray - A color defined in an rgb(+a) array
     * @returns The color as Hex string
     */
    function rgbaArrayToHex(rgbaArray) {
      const r = intToHex(rgbaArray[0]);
      const g = intToHex(rgbaArray[1]);
      const b = intToHex(rgbaArray[2]);
      const a = intToHex(rgbaArray[3] ?? 255);
      return '#' + r + g + b + a;
    }
  })('QBit');

  (function GhostCanvas() {
    const QBit = globalThis[arguments[0]];
    const Await = QBit.findGlobal('Await');
    const Pixel = QBit.findGlobal('Pixel');

    QBit.Styles.addRule(
      '.ghostimage { position:fixed; top:50%; left:50%; opacity:.6; box-shadow: 0 0 1px 1px cornflowerblue inset; }'
    );

    function getBoundingClientRect(htmlElement) {
      let { top, right, bottom, left, width, height, x, y } = htmlElement.getBoundingClientRect();

      top = Math.round(top);
      right = Math.round(right);
      bottom = Math.round(bottom);
      left = Math.round(left);
      width = Math.round(width);
      height = Math.round(height);
      x = Math.round(x);
      y = Math.round(y);

      return { top, right, bottom, left, width, height, x, y };
    }

    function makeDragable(draggableElement, update) {
      let previousX = 0;
      let previousY = 0;
      draggableElement.addEventListener('mousedown', dragMouseDown);

      function dragMouseDown(event) {
        event.preventDefault();
        previousX = event.clientX;
        previousY = event.clientY;
        document.addEventListener('mouseup', closeDragElement, { once: true });
        document.addEventListener('mousemove', elementDrag);
      }

      function elementDrag(event) {
        event.preventDefault();
        const deltaX = previousX - event.clientX;
        const deltaY = previousY - event.clientY;
        previousX = event.clientX;
        previousY = event.clientY;
        draggableElement.style.top = `${Math.round(draggableElement.offsetTop - deltaY)}px`;
        draggableElement.style.left = `${Math.round(draggableElement.offsetLeft - deltaX)}px`;
        update();
      }

      function closeDragElement() {
        document.removeEventListener('mousemove', elementDrag);
      }
    }

    const radios = [];

    class GhostCanvas extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'AutoDraw');
      static isFavorite = true;

      GameCanvas;
      DrawCanvas;
      DrawCanvasContext;
      DrawCanvasRect;
      loadedImages;

      constructor() {
        super('GhostCanvas', '<i class="fas fa-images"></i>');
        this.GameCanvas = document.body.querySelector('canvas#canvas');
        this.DrawCanvas = document.createElement('canvas');
        this.DrawCanvasRect = {};
        this.loadedImages = [];
        this.DrawCanvasContext = this.DrawCanvas.getContext('2d');

        this.#onStartup();
        // loadExtension assigns the parent after construction; defer work that depends on it.
        queueMicrotask(() => this.resetAllSettings());
      }

      #onStartup() {
        this.#loadInterface();
        this.DrawCanvas.width = this.GameCanvas.width;
        this.DrawCanvas.height = this.GameCanvas.height;
        this.DrawCanvas.style =
          'position:fixed; opacity:.6; box-shadow: 0 0 1px 1px firebrick inset; pointer-events: none;';

        const onResize = new Await(this.alignDrawCanvas.bind(this), 500);
        window.addEventListener('resize', (event) => {
          onResize.call();
        });

        this.htmlElements.input.addEventListener('change', (event) => {
          if (this.htmlElements.input.checked) this.alignDrawCanvas();
        });
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
        this.#row3();
        this.#row4();
        this.#row5();
      }

      #row1() {
        const row = domMake.Row();
        {
          const resetSettingsButton = domMake.Button('Reset');
          const showCanvasInput = domMake.Tree('input', {
            type: 'checkbox',
            title: 'Toggle Canvas',
            class: 'icon',
          });
          const clearCanvasButton = domMake.Button('Clear');

          resetSettingsButton.title = 'Reset Settings';
          clearCanvasButton.title = 'Clear GameCanvas';

          resetSettingsButton.addEventListener('click', (event) => {
            this.resetAllSettings();
          });

          showCanvasInput.addEventListener('change', () => {
            this.DrawCanvas.style.display = showCanvasInput.checked ? 'block' : 'none';
          });

          clearCanvasButton.addEventListener('click', (event) => {
            let data = ['drawcmd', 0, [0.5, 0.5, 0.5, 0.5, !0, -2000, '#FFFFFF', -1, !1]];
            const bot = this.findGlobal('BotClientInterface')?.siblings?.find((botInterface) =>
              botInterface.bot.getReadyState()
            )?.bot;
            if (!bot) return this.notify('warning', 'No connected bot is available.');
            bot.send(`${42}${JSON.stringify(data)}`);
          });

          document.body.appendChild(this.DrawCanvas);
          row.appendAll(resetSettingsButton, showCanvasInput, clearCanvasButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          const loadPixelDataButton = domMake.Button('Load');
          const pixelsLeftToDraw = domMake.Tree('input', {
            type: 'text',
            readonly: true,
            style: 'text-align: center;',
            value: '0',
          });
          const clearPixelListButton = domMake.Button('Clear');

          this.htmlElements.pixelsLeftToDraw = pixelsLeftToDraw;
          loadPixelDataButton.title = 'Load Pixels to draw';
          clearPixelListButton.title = 'Clear Pixels to draw';

          loadPixelDataButton.addEventListener('click', (event) => {
            this.saveCanvas();
          });

          clearPixelListButton.addEventListener('click', (event) => {
            this.setPixelList([]);
          });

          row.appendAll(loadPixelDataButton, pixelsLeftToDraw, clearPixelListButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row3() {
        const row = domMake.Row();
        {
          const startDrawingButton = domMake.Button('Start');
          const stopDrawingButton = domMake.Button('Stop');

          startDrawingButton.addEventListener('click', (event) => {
            this.parent.startDrawing();
          });
          stopDrawingButton.addEventListener('click', (event) => {
            this.parent.stopDrawing();
          });

          row.appendAll(startDrawingButton, stopDrawingButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row4() {
        const row = domMake.Row();
        {
          const brushSizeInput = domMake.Tree('input', {
            type: 'number',
            min: 2,
            value: 2,
            max: 200,
            step: 1,
          });
          const singleColorModeInput = domMake.Tree('input', { type: 'checkbox', class: 'icon' });
          const brushColorInput = domMake.Tree('input', { type: 'text', value: 'blue' });

          brushSizeInput.addEventListener('change', (event) => {
            // this.autodraw.brushSize = Number(brushSizeInput.value);
          });
          singleColorModeInput.addEventListener('change', (event) => {
            // this.autodraw.singleColor = Boolean(singleColorModeInput.checked);
          });
          brushColorInput.addEventListener('change', (event) => {
            // this.autodraw.brushColor = brushColorInput;
          });

          row.appendAll(brushSizeInput, singleColorModeInput, brushColorInput);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row5() {
        const row = domMake.IconList();
        {
          const id = generate.uuidv4();
          const chooseGhostlyImageInput = domMake.Tree('input', { type: 'file', id: id, hidden: true });
          const chooseGhostlyImageLabel = domMake.Tree('label', { for: id, class: 'icon', title: 'Add Image' }, [
            domMake.Tree('i', { class: 'fas fa-plus' }),
          ]);

          const localThis = this;
          function onChange() {
            if (!this.files || !this.files[0]) return;
            const myFileReader = new FileReader();
            myFileReader.addEventListener('load', (event) => {
              const base64 = event.target.result.replace('image/gif', 'image/png');
              localThis.createGhostImage(base64, row);
            });
            myFileReader.readAsDataURL(this.files[0]);
          }
          chooseGhostlyImageInput.addEventListener('change', onChange);

          row.appendAll(chooseGhostlyImageLabel, chooseGhostlyImageInput);
        }
        {
          const resetImageSelectionLabel = domMake.Tree('div', { class: 'icon', title: 'Unselect' }, [
            domMake.Tree('i', { class: 'fas fa-chevron-left' }),
          ]);
          resetImageSelectionLabel.addEventListener('click', () => {
            document.body.querySelectorAll('input[name="ghostimage"]').forEach((node) => {
              node.checked = false;
            });
          });
          row.appendChild(resetImageSelectionLabel);
        }
        this.htmlElements.section.appendChild(row);
      }

      createGhostImage(imageSource, row) {
        this.alignDrawCanvas();
        const image = this.loadExtension(GhostImage, (reference) => {
          this.loadedImages.push(reference);
        });
        row.appendChild(image.htmlElements.label);
        image.setImageSource(imageSource);
      }

      clearCanvas() {
        this.DrawCanvasContext.clearRect(0, 0, this.DrawCanvas.width, this.DrawCanvas.height);
      }

      saveCanvas() {
        this.getAllPixels();
      }

      resetAllSettings() {
        this.clearCanvas();
        this.loadedImages.forEach((image, index) => {
          setTimeout(() => {
            image.reduceToAtoms();
          }, 10 * index);
        });
        // this.autodraw?.stopDrawing();
        this.setPixelList([]);
        this.alignDrawCanvas(true);
      }

      alignDrawCanvas() {
        if (arguments[0]) {
          this.DrawCanvas.width = this.GameCanvas.width;
          this.DrawCanvas.height = this.GameCanvas.height;
        }

        const GameCanvasRect = getBoundingClientRect(this.GameCanvas);

        this.DrawCanvas.style.top = `${GameCanvasRect.top}px`;
        this.DrawCanvas.style.left = `${GameCanvasRect.left}px`;
        this.DrawCanvas.style.width = `${GameCanvasRect.width}px`;
        this.DrawCanvas.style.height = `${GameCanvasRect.height}px`;

        const DrawCanvasRect = getBoundingClientRect(this.DrawCanvas);

        if (DrawCanvasRect.width <= 0 || DrawCanvasRect.height <= 0)
          return Object.assign(this.DrawCanvasRect, DrawCanvasRect);
        // DrawCanvasRect.alignModifierX = Number(this.DrawCanvas.width / DrawCanvasRect.width).toFixed(2);
        // DrawCanvasRect.alignModifierY = Number(this.DrawCanvas.height / DrawCanvasRect.height).toFixed(2);

        DrawCanvasRect.drawModifierX = 100 / DrawCanvasRect.width;
        DrawCanvasRect.drawModifierY = 100 / DrawCanvasRect.height;
        Object.assign(this.DrawCanvasRect, DrawCanvasRect);
      }

      getAllPixels() {
        const image = this.DrawCanvasContext.getImageData(
          0,
          0,
          this.DrawCanvasContext.canvas.width,
          this.DrawCanvasContext.canvas.height
        );
        const pixels = [];

        for (let index = 0; index < image.data.length; index += 4) {
          // const x = (index * 0.25) % image.width;
          // const y = Math.floor((index * 0.25) / image.width);
          const x = (index * 0.25) % image.width;
          const y = Math.floor((index * 0.25) / image.width);

          const r = image.data[index + 0];
          const g = image.data[index + 1];
          const b = image.data[index + 2];
          const a = image.data[index + 3];
          // const color = rgbaArrayToHex([r, g, b, a]);
          const color = [r, g, b, a];
          pixels.push(new Pixel(x, y, color));
        }

        this.setPixelList(pixels);
      }

      getNoneTransparentPixels() {
        this.getAllPixels();
        const newPixelArray = this.getPixelList().filter((pixel) => pixel.color[3] > 0);
        this.setPixelList(newPixelArray);
      }

      getPixelList() {
        return this.parent.executionList;
      }

      setPixelList(pixelArray = []) {
        this.parent.executionList = Array.isArray(pixelArray) ? pixelArray : [];
        this.htmlElements.pixelsLeftToDraw.value = this.parent.executionList.length;
      }
    }

    class GhostImage extends QBit {
      image;
      rect;

      constructor() {
        super('GhostImage', '<i class="fas fa-image-polaroid"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();

        this.image = domMake.Tree('img', { class: 'ghostimage' });
        this.image.addEventListener('mousedown', (event) => {
          this.htmlElements.label.click();
        });

        this.htmlElements.input.type = 'radio';
        this.htmlElements.input.name = 'ghostimage';

        radios.push(this.htmlElements.input);
        this.htmlElements.input.addEventListener('change', (event) => {
          radios.forEach(function (radio) {
            document.body.querySelector(`label[for="${radio.id}"]`).classList.remove('active');
          });
          this.htmlElements.label.classList.add('active');
        });

        document.body.appendChild(this.image);
        makeDragable(this.image, this.updatePosition.bind(this));
        this.updatePosition();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          const paintCanvasButton = domMake.Button('Place');

          paintCanvasButton.addEventListener('click', (event) => {
            this.drawImage();
          });

          row.appendAll(paintCanvasButton);
        }
        {
          const enableButton = domMake.Button('Delete');

          enableButton.addEventListener('click', (event) => {
            this.reduceToAtoms();
          });
          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          const scaleInput = domMake.Tree('input', {
            type: 'number',
            title: 'rotation',
            min: 0.1,
            max: 10,
            value: 1,
            step: 0.02,
          });

          scaleInput.addEventListener('change', () => {
            this.image.style.scale = scaleInput.value;
          });

          this.htmlElements.scaleInput = scaleInput;

          row.appendAll(scaleInput);
        }
        {
          const rotationInput = domMake.Tree('input', {
            type: 'number',
            title: 'rotation',
            value: 0,
            step: 1,
          });

          rotationInput.addEventListener('change', () => {
            this.image.style.rotate = `${rotationInput.value}deg`;
          });

          this.htmlElements.rotationInput = rotationInput;

          row.appendChild(rotationInput);
        }
        this.htmlElements.section.appendChild(row);
      }

      drawImage() {
        this.updatePosition();
        const ctx = this.parent.DrawCanvasContext;

        const offsetTop = Number(this.rect.top) - Number(this.parent.DrawCanvasRect.top);
        const offsetLeft = Number(this.rect.left) - Number(this.parent.DrawCanvasRect.left);

        // const multiX = Number(this.parent.DrawCanvasRect.alignModifierX);
        // const multiY = Number(this.parent.DrawCanvasRect.alignModifierY);

        const angle = (Math.PI / 180) * Number(this.htmlElements.rotationInput.value);
        const scale = Number(this.htmlElements.scaleInput.value);

        const imageWidth = this.image.width * scale;
        const imageHeight = this.image.height * scale;
        const imgHalfWidth = imageWidth * 0.5;
        const imgHalfHeight = imageHeight * 0.5;

        ctx.save();
        ctx.translate(offsetLeft + imgHalfWidth, offsetTop + imgHalfHeight);
        ctx.rotate(angle);
        ctx.translate(-imgHalfWidth, -imgHalfHeight);
        ctx.drawImage(this.image, 0, 0, imageWidth, imageHeight);
        ctx.restore();
      }

      setImageSource(imageSource) {
        this.image.src = imageSource;
        this.setIcon(`<img src="${this.image.src}">`);
      }

      updatePosition() {
        this.rect = getBoundingClientRect(this.image);
      }

      reduceToAtoms() {
        this.image.remove();
        const pos = radios.indexOf(this.htmlElements.input);
        if (~pos) radios.splice(pos, 1);

        let pos2 = this.parent.loadedImages.indexOf(this);
        if (~pos2) {
          this.parent.loadedImages.splice(pos2, 1);
        }
        this._EXP_destroy(!0);
      }
    }
  })('QBit');

  (function GhostCanvasAlgorithms() {
    const QBit = globalThis[arguments[0]];

    function sortByColor(pixel1, pixel2) {
      const color1 = rgbaArrayToHex(pixel1.color);
      const color2 = rgbaArrayToHex(pixel2.color);
      if (color1 < color2) {
        return -1;
      }
      if (color1 > color2) {
        return 1;
      }
      return 0;
    }

    function intToHex(number) {
      return number.toString(16).padStart(2, '0');
    }

    function rgbaArrayToHex(rgbaArray) {
      const r = intToHex(rgbaArray[0]);
      const g = intToHex(rgbaArray[1]);
      const b = intToHex(rgbaArray[2]);
      const a = intToHex(rgbaArray[3]);
      return '#' + r + g + b + a;
    }

    function areSameColor(colorArray1, colorArray2, allowedDifference = 8) {
      var red = colorArray1[0] - colorArray2[0];
      var green = colorArray1[1] - colorArray2[1];
      var blue = colorArray1[2] - colorArray2[2];

      if (red < 0) red = red * -1;
      if (green < 0) green = green * -1;
      if (blue < 0) blue = blue * -1;

      if (red > allowedDifference || green > allowedDifference || blue > allowedDifference) return false;
      return true;
    }

    class GhostCanvasMinify extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'GhostCanvas');

      constructor() {
        super('Minify', '<i class="fas fa-compress-arrows-alt"></i>');
        this.minOpacity = 20;
        this.maxFuzzyness = 32;
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
        this.#row3();
        this.#row4();
      }

      #row1() {
        const row = domMake.Row();
        {
          const fuzzynessInput = domMake.Tree('input', {
            type: 'number',
            title: 'Fuzzyness',
            step: 1,
            min: 0,
            max: 255,
            value: 10,
          });
          const opacityInput = domMake.Tree('input', {
            type: 'number',
            title: 'Opacity',
            step: 1,
            min: 0,
            max: 255,
            value: 30,
          });

          fuzzynessInput.addEventListener('change', () => {
            this.maxFuzzyness = Number(fuzzynessInput.value);
          });

          opacityInput.addEventListener('change', () => {
            this.minOpacity = Number(opacityInput.value);
          });

          row.appendAll(fuzzynessInput, opacityInput);
        }
        this.htmlElements.section.appendChild(row);
      }
      #row2() {
        const row = domMake.Row();
        {
          const minifyPixelsArrayButton = domMake.Button('Minify');

          minifyPixelsArrayButton.addEventListener('click', (event) => {
            this.minifyPixelsArray();
          });

          row.appendAll(minifyPixelsArrayButton);
        }
        this.htmlElements.section.appendChild(row);
      }
      #row3() {}
      #row4() {}

      minifyPixelsArray() {
        const pixelArray = this.parent.getPixelList();
        const newPixelArray = [];
        if (!pixelArray.length) return this.parent.setPixelList(newPixelArray);

        let currentPixel = pixelArray[0];
        let lastPixel = currentPixel;
        let currentLine = currentPixel.color[3] >= this.minOpacity ? currentPixel : null;

        for (let index = 1; index < pixelArray.length; index++) {
          currentPixel = pixelArray[index];

          if (lastPixel.color[3] < this.minOpacity && currentPixel.color[3] >= this.minOpacity) {
            // From Transparent To Solid

            currentLine = currentPixel;
          } else if (lastPixel.color[3] >= this.minOpacity && currentPixel.color[3] < this.minOpacity) {
            // From Solid To Transparent

            currentLine.x2 = lastPixel.x2;
            newPixelArray.push(currentLine);
            currentLine = null;
          } else if (currentPixel.color[3] >= this.minOpacity && lastPixel.color[3] >= this.minOpacity) {
            // From Solid To Solid

            if (
              currentLine.y1 !== currentPixel.y1 ||
              lastPixel.x2 !== currentPixel.x1 - 1 ||
              !areSameColor(lastPixel.color, currentPixel.color, this.maxFuzzyness)
            ) {
              currentLine.x2 = lastPixel.x2;
              newPixelArray.push(currentLine);
              currentLine = currentPixel;
            }
          } else {
            // From Transparent To Transparent
          }

          lastPixel = currentPixel;
        }
        if (currentLine) {
          currentLine.x2 = lastPixel.x2;
          newPixelArray.push(currentLine);
        }

        this.parent.setPixelList(newPixelArray);
      }

      minifyPixelsArray_alt() {
        const pixelArray = this.parent.getPixelList();
        const newPixelArray = [];
        if (!pixelArray.length) return this.parent.setPixelList(newPixelArray);
        var lastPixel = pixelArray[0];
        var currentLine = lastPixel;
        const stepsize = this.parent.stepsize ?? 1;

        for (let i = 0; i < pixelArray.length; i += stepsize) {
          const currentPixel = pixelArray[i];

          if (currentPixel.y1 !== currentLine.y1 || currentPixel.color !== lastPixel.color) {
            currentLine.x2 = lastPixel.x2;
            if (!/^#[0-9a-fA-F]{6}[0-4]{2}$/.test(lastPixel.color)) newPixelArray.push(currentLine);
            currentLine = currentPixel;
          }

          lastPixel = currentPixel;
        }
        newPixelArray.push(currentLine);

        this.parent.setPixelList(newPixelArray);
      }
    }

    class GhostCanvasSort extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'GhostCanvas');

      constructor() {
        super('Sort', '<i class="fas fa-sort-numeric-down"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
        this.#row3();
        this.#row4();
      }

      #row1() {
        const row = domMake.Row();
        {
          const sortPixelsArrayButton = domMake.Button('Sort');

          sortPixelsArrayButton.addEventListener('click', (event) => {
            this.sortPixelsArray();
          });

          row.appendAll(sortPixelsArrayButton);
        }
        this.htmlElements.section.appendChild(row);
      }
      #row2() {}
      #row3() {}
      #row4() {}

      sortPixelsArray() {
        const pixelArray = this.parent.getPixelList();

        const newPixelArray = [...pixelArray].sort(sortByColor);

        this.parent.setPixelList(newPixelArray);
      }
    }
  })('QBit');

  (function AutoTranslate() {
    const QBit = globalThis[arguments[0]];

    const unicodeLanguagePatterns = new Map();
    unicodeLanguagePatterns.set('Common', /\p{Script=Common}+/u); // CommonPattern
    unicodeLanguagePatterns.set('Arabic', /\p{Script=Arabic}+/u); // ArabicPattern
    unicodeLanguagePatterns.set('Armenian', /\p{Script=Armenian}+/u); // ArmenianPattern
    unicodeLanguagePatterns.set('Bengali', /\p{Script=Bengali}+/u); // BengaliPattern
    unicodeLanguagePatterns.set('Bopomofo', /\p{Script=Bopomofo}+/u); // BopomofoPattern
    unicodeLanguagePatterns.set('Braille', /\p{Script=Braille}+/u); // BraillePattern
    unicodeLanguagePatterns.set('Buhid', /\p{Script=Buhid}+/u); // BuhidPattern
    unicodeLanguagePatterns.set('Canadian_Aboriginal', /\p{Script=Canadian_Aboriginal}+/u); // Canadian_AboriginalPattern
    unicodeLanguagePatterns.set('Cherokee', /\p{Script=Cherokee}+/u); // CherokeePattern
    unicodeLanguagePatterns.set('Cyrillic', /\p{Script=Cyrillic}+/u); // CyrillicPattern
    unicodeLanguagePatterns.set('Devanagari', /\p{Script=Devanagari}+/u); // DevanagariPattern
    unicodeLanguagePatterns.set('Ethiopic', /\p{Script=Ethiopic}+/u); // EthiopicPattern
    unicodeLanguagePatterns.set('Georgian', /\p{Script=Georgian}+/u); // GeorgianPattern
    unicodeLanguagePatterns.set('Greek', /\p{Script=Greek}+/u); // GreekPattern
    unicodeLanguagePatterns.set('Gujarati', /\p{Script=Gujarati}+/u); // GujaratiPattern
    unicodeLanguagePatterns.set('Gurmukhi', /\p{Script=Gurmukhi}+/u); // GurmukhiPattern
    unicodeLanguagePatterns.set('Han', /\p{Script=Han}+/u); // HanPattern
    unicodeLanguagePatterns.set('Hangul', /\p{Script=Hangul}+/u); // HangulPattern
    unicodeLanguagePatterns.set('Hanunoo', /\p{Script=Hanunoo}+/u); // HanunooPattern
    unicodeLanguagePatterns.set('Hebrew', /\p{Script=Hebrew}+/u); // HebrewPattern
    unicodeLanguagePatterns.set('Hiragana', /\p{Script=Hiragana}+/u); // HiraganaPattern
    unicodeLanguagePatterns.set('Inherited', /\p{Script=Inherited}+/u); // InheritedPattern
    unicodeLanguagePatterns.set('Kannada', /\p{Script=Kannada}+/u); // KannadaPattern
    unicodeLanguagePatterns.set('Katakana', /\p{Script=Katakana}+/u); // KatakanaPattern
    unicodeLanguagePatterns.set('Khmer', /\p{Script=Khmer}+/u); // KhmerPattern
    unicodeLanguagePatterns.set('Lao', /\p{Script=Lao}+/u); // LaoPattern
    unicodeLanguagePatterns.set('Latin', /\p{Script=Latin}+/u); // LatinPattern
    unicodeLanguagePatterns.set('Limbu', /\p{Script=Limbu}+/u); // LimbuPattern
    unicodeLanguagePatterns.set('Malayalam', /\p{Script=Malayalam}+/u); // MalayalamPattern
    unicodeLanguagePatterns.set('Mongolian', /\p{Script=Mongolian}+/u); // MongolianPattern
    unicodeLanguagePatterns.set('Myanmar', /\p{Script=Myanmar}+/u); // MyanmarPattern
    unicodeLanguagePatterns.set('Ogham', /\p{Script=Ogham}+/u); // OghamPattern
    unicodeLanguagePatterns.set('Oriya', /\p{Script=Oriya}+/u); // OriyaPattern
    unicodeLanguagePatterns.set('Runic', /\p{Script=Runic}+/u); // RunicPattern
    unicodeLanguagePatterns.set('Sinhala', /\p{Script=Sinhala}+/u); // SinhalaPattern
    unicodeLanguagePatterns.set('Syriac', /\p{Script=Syriac}+/u); // SyriacPattern
    unicodeLanguagePatterns.set('Tagalog', /\p{Script=Tagalog}+/u); // TagalogPattern
    unicodeLanguagePatterns.set('Tagbanwa', /\p{Script=Tagbanwa}+/u); // TagbanwaPattern
    unicodeLanguagePatterns.set('Tamil', /\p{Script=Tamil}+/u); // TamilPattern
    unicodeLanguagePatterns.set('Telugu', /\p{Script=Telugu}+/u); // TeluguPattern
    unicodeLanguagePatterns.set('Thaana', /\p{Script=Thaana}+/u); // ThaanaPattern
    unicodeLanguagePatterns.set('Thai', /\p{Script=Thai}+/u); // ThaiPattern
    unicodeLanguagePatterns.set('Tibetan', /\p{Script=Tibetan}+/u); // TibetanPattern
    unicodeLanguagePatterns.set('Yi', /\p{Script=Yi}+/u); // YiPattern

    const observeDOM = (function () {
      const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
      /**
       * @param {HTMLElement} nodeToObserve
       * @param {Function} callback
       */
      return function (nodeToObserve, callback) {
        if (!nodeToObserve || nodeToObserve.nodeType !== 1) return;

        if (MutationObserver) {
          // define a new observer
          var mutationObserver = new MutationObserver(callback);

          // have the observer observe for changes in children
          mutationObserver.observe(nodeToObserve, { childList: true, subtree: !1 });
          return mutationObserver;
        }

        // browser support fallback
        else if (window.addEventListener) {
          nodeToObserve.addEventListener('DOMNodeInserted', callback, false);
          nodeToObserve.addEventListener('DOMNodeRemoved', callback, false);
        }
      };
    })();

    class AutoTranslate extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, 'CubeEngine');

      active;

      constructor() {
        super('AutoTranslate', '<i class="fas fa-language"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();

        this.active = false;

        const observable = document.querySelector('#chatbox_messages');

        observeDOM(observable, (mutation) => {
          if (!this.active) return;

          const addedNodes = [];
          const removedNodes = [];

          mutation.forEach((record) => {
            if (record.addedNodes.length) addedNodes.push(...record.addedNodes);
            if (record.removedNodes.length) removedNodes.push(...record.removedNodes);
          });

          // console.log('Added:', addedNodes, 'Removed:', removedNodes);

          addedNodes.forEach((node) => {
            if (!(node instanceof Element)) return;
            if (node.classList.contains('systemchatmessage5')) return;
            if (node.querySelector('.playerchatmessage-selfname')) return;
            if (!node.querySelector('.playerchatmessage-text')) return;

            // console.log(node);
            const message = node.querySelector('.playerchatmessage-text');
            const text = message.textContent;
            const language = this.detectLanguage(text);

            if (language)
              this.translate(text, language, 'en', (translation) => {
                applyTitleToChatMessage(translation, message);
              });
          });

          function applyTitleToChatMessage(text, node) {
            node.title = text;
          }
        });
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          const enableButton = domMake.Button('Enable');

          enableButton.addEventListener('click', (event) => {
            this.active ? this.disable() : this.enable();
          });
          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {}

      enable() {
        this.active = true;

        this.htmlElements.toggleStatusButton.classList.add('active');
        this.htmlElements.toggleStatusButton.textContent = 'Active';

        this.notify('success', `enabled`);
      }

      disable() {
        this.active = false;

        this.htmlElements.toggleStatusButton.classList.remove('active');
        this.htmlElements.toggleStatusButton.textContent = 'Inactive';

        this.notify('warning', `disabled`);
      }

      detectLanguage(text) {
        if (unicodeLanguagePatterns.get('Cyrillic').test(text)) return 'ru';
        if (unicodeLanguagePatterns.get('Arabic').test(text)) return 'ar';
        if (unicodeLanguagePatterns.get('Greek').test(text)) return 'el';
      }

      translate(textToTranslate, from = 'ru', to = 'en', callback) {
        const sourceText = textToTranslate;
        const sourceLang = from;
        const targetLang = to;

        const url =
          'https://translate.googleapis.com/translate_a/single?client=gtx&sl=' +
          sourceLang +
          '&tl=' +
          targetLang +
          '&dt=t&q=' +
          encodeURIComponent(sourceText);

        xhrGetJson(url, log);

        function log(data) {
          callback(data[0][0][0]);
        }
      }
    }

    function xhrGetJson(url, callback) {
      const req = new XMLHttpRequest();

      req.onload = (e) => {
        if (req.status < 200 || req.status >= 300) return;
        const response = req.response; // not responseText
        if (!callback) return;
        try {
          callback(JSON.parse(response));
        } catch (error) {
          console.log(error);
        }
      };
      req.onerror = () => console.warn(`Cubic Engine: Request failed for ${url}.`);
      req.open('GET', url);
      req.send();
    }
  })('QBit');
})();