Greasy Fork is available in English.

Miracle Scripts

Bookmark your favorite skins! Use skin shortcuts! Go crazy with cell animations! Improved chatlog! Dance! Use chat replacements! Display the time!

Mint 2020.01.16.. Lásd a legutóbbi verzió

// ==UserScript==
// @name         Miracle Scripts
// @namespace    Miracle Scripts
// @version      1.7.0
// @description  Bookmark your favorite skins! Use skin shortcuts! Go crazy with cell animations! Improved chatlog! Dance! Use chat replacements! Display the time!
// @homepage     http://agarioforums.net/member.php?action=profile&uid=21263
// @author       You
// @license      MIT
// @icon         https://abload.de/img/mh3k8o.png
// @match        http://agma.io/
// @grant        none
// ==/UserScript==

// Source: http://stackoverflow.com/questions/1772179/get-character-value-from-keycode-in-javascript-then-trim#answer-23377822
window.keyboardMap = [
    "", // [0]
    "", // [1]
    "", // [2]
    "CANCEL", // [3]
    "", // [4]
    "", // [5]
    "HELP", // [6]
    "", // [7]
    "BACK_SPACE", // [8]
    "TAB", // [9]
    "", // [10]
    "", // [11]
    "CLEAR", // [12]
    "ENTER", // [13]
    "ENTER_SPECIAL", // [14]
    "", // [15]
    "SHIFT", // [16]
    "CONTROL", // [17]
    "ALT", // [18]
    "PAUSE", // [19]
    "CAPS_LOCK", // [20]
    "KANA", // [21]
    "EISU", // [22]
    "JUNJA", // [23]
    "FINAL", // [24]
    "HANJA", // [25]
    "", // [26]
    "ESCAPE", // [27]
    "CONVERT", // [28]
    "NONCONVERT", // [29]
    "ACCEPT", // [30]
    "MODECHANGE", // [31]
    "SPACE", // [32]
    "PAGE_UP", // [33]
    "PAGE_DOWN", // [34]
    "END", // [35]
    "HOME", // [36]
    "LEFT", // [37]
    "UP", // [38]
    "RIGHT", // [39]
    "DOWN", // [40]
    "SELECT", // [41]
    "PRINT", // [42]
    "EXECUTE", // [43]
    "PRINTSCREEN", // [44]
    "INSERT", // [45]
    "DELETE", // [46]
    "", // [47]
    "0", // [48]
    "1", // [49]
    "2", // [50]
    "3", // [51]
    "4", // [52]
    "5", // [53]
    "6", // [54]
    "7", // [55]
    "8", // [56]
    "9", // [57]
    "COLON", // [58]
    "SEMICOLON", // [59]
    "LESS_THAN", // [60]
    "EQUALS", // [61]
    "GREATER_THAN", // [62]
    "QUESTION_MARK", // [63]
    "AT", // [64]
    "A", // [65]
    "B", // [66]
    "C", // [67]
    "D", // [68]
    "E", // [69]
    "F", // [70]
    "G", // [71]
    "H", // [72]
    "I", // [73]
    "J", // [74]
    "K", // [75]
    "L", // [76]
    "M", // [77]
    "N", // [78]
    "O", // [79]
    "P", // [80]
    "Q", // [81]
    "R", // [82]
    "S", // [83]
    "T", // [84]
    "U", // [85]
    "V", // [86]
    "W", // [87]
    "X", // [88]
    "Y", // [89]
    "Z", // [90]
    "OS_KEY", // [91] Windows Key (Windows) or Command Key (Mac)
    "", // [92]
    "CONTEXT_MENU", // [93]
    "", // [94]
    "SLEEP", // [95]
    "NUMPAD0", // [96]
    "NUMPAD1", // [97]
    "NUMPAD2", // [98]
    "NUMPAD3", // [99]
    "NUMPAD4", // [100]
    "NUMPAD5", // [101]
    "NUMPAD6", // [102]
    "NUMPAD7", // [103]
    "NUMPAD8", // [104]
    "NUMPAD9", // [105]
    "MULTIPLY", // [106]
    "ADD", // [107]
    "SEPARATOR", // [108]
    "SUBTRACT", // [109]
    "DECIMAL", // [110]
    "DIVIDE", // [111]
    "F1", // [112]
    "F2", // [113]
    "F3", // [114]
    "F4", // [115]
    "F5", // [116]
    "F6", // [117]
    "F7", // [118]
    "F8", // [119]
    "F9", // [120]
    "F10", // [121]
    "F11", // [122]
    "F12", // [123]
    "F13", // [124]
    "F14", // [125]
    "F15", // [126]
    "F16", // [127]
    "F17", // [128]
    "F18", // [129]
    "F19", // [130]
    "F20", // [131]
    "F21", // [132]
    "F22", // [133]
    "F23", // [134]
    "F24", // [135]
    "", // [136]
    "", // [137]
    "", // [138]
    "", // [139]
    "", // [140]
    "", // [141]
    "", // [142]
    "", // [143]
    "NUM_LOCK", // [144]
    "SCROLL_LOCK", // [145]
    "WIN_OEM_FJ_JISHO", // [146]
    "WIN_OEM_FJ_MASSHOU", // [147]
    "WIN_OEM_FJ_TOUROKU", // [148]
    "WIN_OEM_FJ_LOYA", // [149]
    "WIN_OEM_FJ_ROYA", // [150]
    "", // [151]
    "", // [152]
    "", // [153]
    "", // [154]
    "", // [155]
    "", // [156]
    "", // [157]
    "", // [158]
    "", // [159]
    "CIRCUMFLEX", // [160]
    "EXCLAMATION", // [161]
    "DOUBLE_QUOTE", // [162]
    "HASH", // [163]
    "DOLLAR", // [164]
    "PERCENT", // [165]
    "AMPERSAND", // [166]
    "UNDERSCORE", // [167]
    "OPEN_PAREN", // [168]
    "CLOSE_PAREN", // [169]
    "ASTERISK", // [170]
    "PLUS", // [171]
    "PIPE", // [172]
    "HYPHEN_MINUS", // [173]
    "OPEN_CURLY_BRACKET", // [174]
    "CLOSE_CURLY_BRACKET", // [175]
    "TILDE", // [176]
    "", // [177]
    "", // [178]
    "", // [179]
    "", // [180]
    "VOLUME_MUTE", // [181]
    "VOLUME_DOWN", // [182]
    "VOLUME_UP", // [183]
    "", // [184]
    "", // [185]
    "SEMICOLON", // [186]
    "EQUALS", // [187]
    "COMMA", // [188]
    "MINUS", // [189]
    "PERIOD", // [190]
    "SLASH", // [191]
    "BACK_QUOTE", // [192]
    "", // [193]
    "", // [194]
    "", // [195]
    "", // [196]
    "", // [197]
    "", // [198]
    "", // [199]
    "", // [200]
    "", // [201]
    "", // [202]
    "", // [203]
    "", // [204]
    "", // [205]
    "", // [206]
    "", // [207]
    "", // [208]
    "", // [209]
    "", // [210]
    "", // [211]
    "", // [212]
    "", // [213]
    "", // [214]
    "", // [215]
    "", // [216]
    "", // [217]
    "", // [218]
    "OPEN_BRACKET", // [219]
    "BACK_SLASH", // [220]
    "CLOSE_BRACKET", // [221]
    "QUOTE", // [222]
    "", // [223]
    "META", // [224]
    "ALTGR", // [225]
    "", // [226]
    "WIN_ICO_HELP", // [227]
    "WIN_ICO_00", // [228]
    "", // [229]
    "WIN_ICO_CLEAR", // [230]
    "", // [231]
    "", // [232]
    "WIN_OEM_RESET", // [233]
    "WIN_OEM_JUMP", // [234]
    "WIN_OEM_PA1", // [235]
    "WIN_OEM_PA2", // [236]
    "WIN_OEM_PA3", // [237]
    "WIN_OEM_WSCTRL", // [238]
    "WIN_OEM_CUSEL", // [239]
    "WIN_OEM_ATTN", // [240]
    "WIN_OEM_FINISH", // [241]
    "WIN_OEM_COPY", // [242]
    "WIN_OEM_AUTO", // [243]
    "WIN_OEM_ENLW", // [244]
    "WIN_OEM_BACKTAB", // [245]
    "ATTN", // [246]
    "CRSEL", // [247]
    "EXSEL", // [248]
    "EREOF", // [249]
    "PLAY", // [250]
    "ZOOM", // [251]
    "", // [252]
    "PA1", // [253]
    "WIN_OEM_CLEAR", // [254]
    "" // [255]
];

// Don't remove the spaces, they are used as seperators! Source: https://emojiterra.com/de/liste/
window.emojis = '😀 😃 😄 😁 😆 😅 😂 😉 😊 😇 😍 😘 😗 ☺️ 😚 😙 😋 😛 😜 😝 😐 😑 😶 😏 😒 😬 😌 😔 😪 😴 😷 😵 😎 😕 😟 😮 😯 😲 😳 😦 😧 😨 😰 😥 😢 😭 😱 😖 😣 😞 😓 😩 😫 😤 😡 😠 😈 👿 💀 💩 👹 👺 👻 👽 👾 😺 😸 😹 😻 😼 😽 🙀 😿 😾 🙈 🙉 🙊 💋 💌 💘 💝 💖 💗 💓 💞 💕 💟 💔 ❤️ 💛 💚 💙 💜 💯 💢 💥 💫 💦 💨 💣 💬 💭 💤 👋 ✋ 👌 ✌️ 👈 👉 👆 👇 ☝️ 👍 👎 ✊ 👊 👏 🙌 👐 🙏 💅 💪 👂 👃 👀 👅 👄 👶 👦 👧 👱 👨 👩 👴 👵 🙍 🙎 🙅 🙆 💁 🙋 🙇 👮 💂 👷 👸 👳 👲 👰 👼 🎅 💆 💇 🚶 🏃 💃 👯 🏇 🏂 🏄 🚣 🏊 🚴 🚵 🛀 👭 👫 👬 💏 💑 👪 👤 👥 👣 🐵 🐒 🐶 🐕 🐩 🐺 🐱 🐈 🐯 🐅 🐆 🐴 🐎 🐮 🐂 🐃 🐄 🐷 🐖 🐗 🐽 🐏 🐑 🐐 🐪 🐫 🐘 🐭 🐁 🐀 🐹 🐰 🐇 🐻 🐨 🐼 🐾 🐔 🐓 🐣 🐤 🐥 🐦 🐧 🐸 🐊 🐢 🐍 🐲 🐉 🐳 🐋 🐬 🐟 🐠 🐡 🐙 🐚 🐌 🐛 🐜 🐝 🐞 💐 🌸 💮 🌹 🌺 🌻 🌼 🌷 🌱 🌲 🌳 🌴 🌵 🌾 🌿 🍀 🍁 🍂 🍃 🍇 🍈 🍉 🍊 🍋 🍌 🍍 🍎 🍏 🍐 🍑 🍒 🍓 🍅 🍆 🌽 🍄 🌰 🍞 🍖 🍗 🍔 🍟 🍕 🍳 🍲 🍱 🍘 🍙 🍚 🍛 🍜 🍝 🍠 🍢 🍣 🍤 🍥 🍡 🍦 🍧 🍨 🍩 🍪 🎂 🍰 🍫 🍬 🍭 🍮 🍯 🍼 ☕ 🍵 🍶 🍷 🍸 🍹 🍺 🍻 🍴 🔪 🌍 🌎 🌏 🌐 🗾 🌋 🗻 🏠 🏡 🏢 🏣 🏤 🏥 🏦 🏨 🏩 🏪 🏫 🏬 🏭 🏯 🏰 💒 🗼 🗽 ⛪ ⛲ ⛺ 🌁 🌃 🌄 🌅 🌆 🌇 🌉 ♨️ 🎠 🎡 🎢 💈 🎪 🚂 🚃 🚄 🚅 🚆 🚇 🚈 🚉 🚊 🚝 🚞 🚋 🚌 🚍 🚎 🚐 🚑 🚒 🚓 🚔 🚕 🚖 🚗 🚘 🚙 🚚 🚛 🚜 🚲 🚏 ⛽ 🚨 🚥 🚦 🚧 ⚓ ⛵ 🚤 🚢 ✈️ 💺 🚁 🚟 🚠 🚡 🚀 ⌛ ⏳ ⌚ ⏰ 🕛 🕧 🕐 🕜 🕑 🕝 🕒 🕞 🕓 🕟 🕔 🕠 🕕 🕡 🕖 🕢 🕗 🕣 🕘 🕤 🕙 🕥 🕚 🕦 🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘 🌙 🌚 🌛 🌜 ☀️ 🌝 🌞 ⭐ 🌟 🌠 🌌 ☁️ ⛅ 🌀 🌈 🌂 ☔ ⚡ ❄️ ⛄ 🔥 💧 🌊 🎃 🎄 🎆 🎇 ✨ 🎈 🎉 🎊 🎋 🎍 🎎 🎏 🎐 🎑 🎀 🎁 🎫 🏆 ⚽ ⚾ 🏀 🏈 🏉 🎾 🎳 ⛳ 🎣 🎽 🎿 🎯 🎱 🔮 🎮 🎰 🎲 ♠️ ♥️ ♦️ ♣️ 🃏 🀄 🎴 🎭 🎨 👓 👔 👕 👖 👗 👘 👙 👚 👛 👜 👝 🎒 👞 👟 👠 👡 👢 👑 👒 🎩 🎓 💄 💍 💎 🔇 🔈 🔉 🔊 📢 📣 📯 🔔 🔕 🎼 🎵 🎶 🎤 🎧 📻 🎷 🎸 🎹 🎺 🎻 📱 📲 ☎️ 📞 📟 📠 🔋 🔌 💻 💽 💾 💿 📀 🎥 🎬 📺 📷 📹 📼 🔍 🔎 💡 🔦 🏮 📔 📕 📖 📗 📘 📙 📚 📓 📒 📃 📜 📄 📰 📑 🔖 💰 💴 💵 💶 💷 💸 💳 💹 💱 💲 ✉️ 📧 📨 📩 📤 📥 📦 📫 📪 📬 📭 📮 ✏️ ✒️ 📝 💼 📁 📂 📅 📆 📇 📈 📉 📊 📋 📌 📍 📎 📏 📐 ✂️ 🔒 🔓 🔏 🔐 🔑 🔨 🔫 🔧 🔩 🔗 🔬 🔭 📡 💉 💊 🚪 🚽 🚿 🛁 🚬 🗿 🏧 🚮 🚰 ♿ 🚹 🚺 🚻 🚼 🚾 🛂 🛃 🛄 🛅 ⚠️ 🚸 ⛔ 🚫 🚳 🚭 🚯 🚱 🚷 📵 🔞 ⬆️ ↗️ ➡️ ↘️ ⬇️ ↙️ ⬅️ ↖️ ↕️ ↔️ 🔃 🔄 🔙 🔚 🔛 🔜 🔝 🔯 ♈ ♉ ♊ ♋ ♌ ♍ ♎ ♏ ♐ ♑ ♒ ♓ ⛎ 🔀 🔁 🔂 ▶️ ◀️ 🔼 🔽 🎦 📶 📳 📴 ♻️ 🔱 📛 🔰 ⭕ ✅ ☑️ ✖️ ❌ ❎ ➕ ➖ ➗ ➰ ➿ 〽️ ✳️ ✴️ ❇️ ‼️ ⁉️ ❓ ❔ ❕ ❗ 〰️ ©️ ®️ ™️ 🔠 🔡 🔢 🔣 🔤 🅰️ 🆎 🅱️ 🆑 🆒 🆓 🆔 Ⓜ️ 🆕 🆖 🅾️ 🆗 🅿️ 🆘 🆙 🆚 🈁 🈂️ 🈷️ 🈶 🈯 🉐 🈹 🈚 🈲 🉑 🈸 🈴 🈳 ㊗️ ㊙️ 🈺 🈵 🔴 🔵 ⚫ ⚪ ⬛ ⬜ ◼️ ◻️ ◾ ◽ ▪️ ▫️ 🔶 🔷 🔸 🔹 🔺 🔻 💠 🔘 🔳 🔲 🏁 🚩 🎌';

(function() {
    'use strict';

    var settings;

    var loadSettings = function(stringifiedSettings) {
        var defaultSettings = {
            // To get keycodes: https://keycode.info
            bindings: {
                animation: 17, // CRTL
                paste: 33, // PAGE UP
                dance: 34, // PAGE DOWN,
                chatLog: 76, // L
            },
            replacements: ":D|:smile:\n:*(|:sob:\n:'D|:sweat_smiley:\nxD|:joy:",
            primaryColor: "#FF69B4",
            targetLanguage: 'EN',
            favSkins: [],
        };

        if (stringifiedSettings == null) {
            settings = defaultSettings;
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
        } else {
            settings = JSON.parse(stringifiedSettings);

            // Update for settigns:
            if (typeof settings.primaryColor === 'undefined') {
                settings.primaryColor = defaultSettings.primaryColor;
                localStorage.setItem('miracleScripts', JSON.stringify(settings));
            }
            if (typeof settings.bindings.chatLog === 'undefined') {
                settings.bindings.chatLog = defaultSettings.bindings.chatLog;
                localStorage.setItem('miracleScripts', JSON.stringify(settings));
            }
            if (typeof settings.favSkins === 'undefined') {
                settings.favSkins = defaultSettings.favSkins;
                localStorage.setItem('miracleScripts', JSON.stringify(settings));
            }
            if (typeof settings.targetLanguage === 'undefined') {
                settings.targetLanguage = defaultSettings.targetLanguage;
                localStorage.setItem('miracleScripts', JSON.stringify(settings));
            }
            if (typeof settings.quickSkins === 'undefined') {
                settings.quickSkins = []
                localStorage.setItem('miracleScripts', JSON.stringify(settings));
            }
        }
    }
    loadSettings(localStorage.getItem('miracleScripts'));

    var applyPrimaryColor = function() {
        var primaryColorCss = '.miracle-primary-color-font { color: ' + settings.primaryColor + ' !important } .miracle-primary-color-background { background-color: ' + settings.primaryColor + ' !important }; ';
        $('body').append('<style>' + primaryColorCss + '</style>');
    };
    applyPrimaryColor();

    var download = function(filename, text) {
        var element = document.createElement('a');
        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
        element.setAttribute('download', filename);

        element.style.display = 'none';
        document.body.appendChild(element);

        element.click();

        document.body.removeChild(element);
    }

    window.curserMessage = function(message) {
      var curser = document.querySelector('#curser');

      curser.textContent = message;
      curser.style.display = 'block';

      window.setTimeout(function() {
        curser.style.display = 'none';
      }, 5000);
    },

    // We need to have a delay, because the menu is not laoded right away
    setTimeout(function() {
        var $playButton = $('#playBtn');
        var $specateButton = $('#spectateBtn');

        $playButton.get(0).style.width = '40%';
        $specateButton.get(0).style.width = '40%';

        var $settingsButton = $('<button class="spec" style="width: 40px; margin-left: 7px; text-align: center; padding: 10px 0 20px 0" title="Miracle Scripts Settings">📜</button>');
        $settingsButton.insertAfter($playButton);

        var changeKey = function(event) {
            var name = this.name.substr(4);
            $(this).val(window.keyboardMap[event.keyCode]);
            settings.bindings[name] = event.keyCode;
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
        };

        var deleteKey =  function(event) {
            var action = $(this).attr('data-action');
            $('#miracle-settings input[name=key_' + action + ']').val('undefined');
            settings.bindings[action] = null;
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
        }

        // Weird Agma scripting... press enter in the replacements textarea and the chatbox gets focused!
        // Therefore catch the keydown event (that happens earlier) and insert the linebreak manually,
        // focus again (delayed) and go to the end of the text where the linebreak is.
        // We can improve this later on...
        var addReturn = function(event) {
            if (event.keyCode == 13) {
                var textarea = this;
                $(textarea).text($(this).text() + '\n').focus();
                setTimeout(function(){
                    $(textarea).focus();
                    textarea.setSelectionRange(textarea.value.length, textarea.value.length);
                }, 1);
            }
        };
        var changeReplacements = function(event) {
            settings.replacements = $(this).val();
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
        };
        var changePrimaryColor = function(event) {
            settings.primaryColor = $(this).val();
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
            applyPrimaryColor();
        };
        var changeTargetLanguage = function(event) {
            settings.targetLanguage = $(this).val();
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
        };

        var $modal = $('<div id="miracle-settings" class="miracle-primary-color-font" style="position: fixed; width: 100%; height: 100%; padding: 50px; background-color: rgba(0,0,0,0.95); z-index: 999; display: none"></div>');
        $modal.append('<h1>Miracle Scripts Settings</h1>');

        if (GM_info) {
            $modal.append('<small style="color: #717171">Version ' + GM_info.script.version + '</small>');
        }

        var $element = $('<input name="key_animation" value="' + window.keyboardMap[settings.bindings.animation] + '"/>').keyup(changeKey);
        $modal.append('<br><br>Animation-Key:<br>', $element);
        $element = $('<a href="#" data-action="animation" class="miracle-primary-color-background" style="display: inline-block; padding: 3px 10px; color: white">✖</a>').click(deleteKey);
        $modal.append($element);

        $element = $('<input name="key_paste" value="' + window.keyboardMap[settings.bindings.paste] + '"/>').keyup(changeKey);
        $modal.append('<br>Paste-Key:<br>', $element);
        $element = $('<a href="#" data-action="paste" class="miracle-primary-color-background" style="display: inline-block; padding: 3px 10px; color: white">✖</a>').click(deleteKey);
        $modal.append($element);

        $element = $('<input name="key_dance" value="' + window.keyboardMap[settings.bindings.dance] + '"/>').keyup(changeKey);
        $modal.append('<br>Dance-Key:<br>', $element);
        $element = $('<a href="#" data-action="dance" class="miracle-primary-color-background" style="display: inline-block; padding: 3px 10px; color: white">✖</a>').click(deleteKey);
        $modal.append($element);

        $element = $('<input name="key_chatLog" value="' + window.keyboardMap[settings.bindings.chatLog] + '"/>').keyup(changeKey);
        $modal.append('<br>Chat-Log-Key:<br>', $element);
        $element = $('<a href="#" data-action="chatLog" class="miracle-primary-color-background" style="display: inline-block; padding: 3px 10px; color: white">✖</a>').click(deleteKey);
        $modal.append($element);

        $element = $('<select name="target_language"><option value="en">English</option><option value="de">German</option><option value="fr">French</option><option value="es">Spanish</option><option value="it">Italian</option><option value="nl">Dutch</option><option value="pl">Polish</option><option value="pt">Portuguese</option><option value="ru">Russian</option></select>').change(changeTargetLanguage);
        $modal.append('<br><br>Translate chat messages to:<br>', $element);
        $element.get(0).value = settings.targetLanguage;

        $element = $('<input type="color" name="favcolor" value="' + settings.primaryColor + '"/>').change(changePrimaryColor);
        $modal.append('<br><br>User interface color:<br>', $element);

        $element = $('<textarea rows="6" style="width: 100%; max-width: 500px" placeholder="search|replace">').text(settings.replacements).keydown(addReturn).keyup(changeReplacements);
        $modal.append('<br><br>Chat-Replacements (1 per line):<br>', $element, '<br>💡 <span style="color: #717171">Avoid using the search text in the replacement!</span>');

        $modal.append($('<br><a href="#" class="miracle-primary-color-background" style="display: inline-block; margin-top: 20px; padding: 10px; color: white">Close</a>').click(function() { $modal.hide() }));
        $modal.append($('<a href="#" class="miracle-primary-color-background" style="display: inline-block; margin-left: 20px; margin-top: 20px; padding: 10px; color: white">Export settings</a>').click(function() { download('miracle_settings.txt', localStorage.getItem('miracleScripts')) }));
        $modal.append($('<a href="#" class="miracle-primary-color-background" style="display: inline-block; margin-left: 20px; margin-top: 20px; padding: 10px; color: white">Import settings</a>').click(function() {
            var stringifiedSettings = prompt('Paste the settings here');
            if (stringifiedSettings !== null) {
                loadSettings(stringifiedSettings);
                localStorage.setItem('miracleScripts', stringifiedSettings);
                $modal.hide();
                alert('Settings loaded! Reload Agma to refresh.');
            }
        }));
        $modal.append($('<a href="http://agarioforums.net/showthread.php?tid=61388" target="_blank" class="miracle-primary-color-background" style="display: inline-block; margin-left: 20px; margin-top: 20px; padding: 10px; color: white">Support</a>'));

        $('body').append($modal);

        $settingsButton.click(function(event)
                              {
            $modal.show();

            event.preventDefault();
        });
    }, 500);
})();


(function() {
    'use strict';

    /**
     * Returns a random number between min (inclusive) and max (exclusive)
     * Source: MDN
     */
    var getRandomArbitrary = function(min, max) {
        return Math.random() * (max - min) + min;
    }

    var chatAnimate = function()
    {
        // All available commands and combinations
        var items = ['wacky',
                     'spin', 'spinspin', 'spinspinspin', 'wackyspin', 'wackyspinspin', 'wackyspinspinspin',
                     'flip', 'flipflip', 'flipflipflip', 'wackyflip', 'wackyflipflip', 'wackyflipflipfip',
                     'shake', 'shakeshake','shakeshakeshake','wackyshake','wackyshakeshake','wackyshakeshakeshake',
                     'jump', 'jumpjump', 'jumpjumpjump', 'wackyjump', 'wackyjumpjump', 'wackyjumpjumpjump',
                    ];

        // Super-combinations!!
        if (getRandomArbitrary(1, 3) == 1) {
            items = ['jumpspinflip', 'jumpflipshake', 'jumpspinshake', 'spinshakeflip'];
        }

        // Choose randomly an item of the items array
        // Source: https://stackoverflow.com/questions/5915096/get-random-item-from-javascript-array
        var item = items[Math.floor(Math.random() * items.length)];

        item += String.fromCharCode(8203).repeat(getRandomArbitrary(1, 5));

        // Add text into the chatbox and focus it (Note: acutally "/" is no longer necessary)
        $('#chtbox').val($('#chtbox').val() + item).focus();

        // Stop the event so that the pressed key won't be written into the chatbox!
        event.preventDefault();
    }

    window.addEventListener('keydown', function(event)
                            {
        // Do nothing if a menu is open
        if (document.getElementById('overlays').style.display != 'none' || document.getElementById('advert').style.display != 'none') {
            return;
        }

        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        if (event.keyCode == settings.bindings.animation) {
            chatAnimate();
        }
    });
})();


(function() {
    'use strict';

    var emojiFontSize = (window.innerWidth * window.innerHeight > 2000000) ? 24 : 18;
    var css = '#miracle-emojis .miracle-emoji { display: inline-block; width: 40px; margin: 0 2px 2px 0; padding: 5px; border: 1px solid #333; font-size: ' + emojiFontSize + 'px; }\n' +
        '#miracle-emojis .miracle-emoji:hover { background-color: #FF69B4 }';

    var emojis = window.emojis.split(' ');
    var emojiCode = '';

    emojis.forEach(function(emoji) {
        emojiCode += '<a href="#" class="miracle-emoji">' + emoji + '</a>';
    });

    var addEmoji = function()
    {
        setTimeout(function(){
            var $pasteInput = $(document).find('#miracle-emojis input[name=paste]');

            // Add text into the chatbox and focus it
            $('#chtbox').val($('#chtbox').val() + $pasteInput.val()).focus();
        }, 200);

        $modal.hide();
    };

    var $modal = $('<div id="miracle-emojis" class="miracle-primary-color-font" style="position: fixed; width: 100%; height: 100%; padding: 50px; color: #FF69B4; background-color: rgba(0,0,0,0.95); overflow-y: auto; z-index: 999; display: none"></div>');
    $modal.append('<style>' + css + '</style>');
    $modal.append('<h1>Insert text or emoji</h1>');
    var $pasteInput = $('<input name="paste" value="" placeholder="Click to paste text, or (double)click emoji!" style="width: 300px; max-width: 100%" />');
    $modal.append('<br><br>Insert:<br>', $pasteInput);
    $modal.html($modal.html() + '<br><br>' + emojiCode);
    $modal.append($('<br><a href="#" class="miracle-primary-color-background" style="display: inline-block; margin-top: 20px; padding: 10px; color: white">Add</a>').click(addEmoji));
    $modal.append($('<a href="#" class="miracle-primary-color-background" style="display: inline-block; float: right; margin-top: 20px; padding: 10px; color: white">Cancel</a>').click(function() { $modal.hide() }));

    $modal.find('input[name=paste]').click(function(event)
                                           {
        var text = prompt('Please paste your text here!');

        if (text !== null) {
            var $pasteInput = $modal.find('input[name=paste]');

            // Add text into the paste input
            $pasteInput.val($pasteInput.val() + text);
        }
    });

    $modal.click(function(event)
                 {
        if (event.target.classList.contains('miracle-emoji')) {
            var $target = $(this).find('input[name=paste]');
            $target.val($target.val() + $(event.target).text());

            event.preventDefault();
        }
    });

    $modal.dblclick(function(event)
                    {
        if (event.target.classList.contains('miracle-emoji')) {
            $('#chtbox').val($('#chtbox').val() + $(event.target).text()).focus();
            $(this).hide();

            event.preventDefault();
        }
    });

    $('body').append($modal);

    window.addEventListener('keydown', function(event)
                            {
        // Do nothing if a menu is open
        if (document.getElementById('overlays').style.display != 'none' || document.getElementById('advert').style.display != 'none') {
            return;
        }

        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        if (event.keyCode == settings.bindings.paste) {
            $modal.find('input[name=paste]').val('');
            $modal.toggle();
        }
    });
})();


(function() {
    'use strict';

    var angle = 0;
    var angleSpeed = 20;
    var distance = Math.floor(Math.min(window.innerWidth, window.innerHeight) / 2);
    var dancing = false;

    var dance = function()
    {
        angle += angleSpeed;
        if (angle > 360) {
            angle = 0;
        }

        var x = window.innerWidth/2 + Math.sin(angle * Math.PI / 180) * distance;
        var y = window.innerHeight/2 + Math.cos(angle * Math.PI / 180) * distance;
        $('canvas').trigger($.Event('mousemove', {clientX: x, clientY: y}));

        // Stop dancing if dead ... to avoid continuing dancing after next respawn
        if (document.getElementById('advert').style.display != 'none') {
            dancing = false;
        }
        if (dancing) {
            window.requestAnimationFrame(dance);
        }
    }

    window.addEventListener('keyup', function() {
        // Do nothing if a menu is open
        if (document.getElementById('overlays').style.display != 'none' || document.getElementById('advert').style.display != 'none') {
            return;
        }

        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        if (event.keyCode == settings.bindings.dance) {
            dancing = ! dancing;

            if (dancing) {
                window.requestAnimationFrame(dance);
            }
        }
    });
})();


(function() {
    'use strict';

    $('#chtbox').keyup(function(event)
                       {
        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        var lines = settings.replacements.split('\n');

        var text = $('#chtbox').val();

        lines.forEach(function(line) {
            var replacement = line.split('|');
            if (replacement.length == 2) {
                text = text.replace(replacement[0], replacement[1]);
                $('#chtbox').val(text).focus();
            }
        });
    });
})();


(function() {
    'use strict';

    // We escape the message before we print them, so no one can inject JS code!
    var htmlEntities = function(str) {
        return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    }

    var originalFillText = CanvasRenderingContext2D.prototype.fillText
    var lastChatNickname = null;
    var lastChatNicknameColor = null;
    var chatLogCode = '';
    var chatLog = [];
    CanvasRenderingContext2D.prototype.fillText = function () {
        if (this.canvas.id != 'leaderboard' && this.canvas.height == 23) {
            var text = arguments[0];

            // Sometimes also numbers (int) are printed so we filter for strings
            if (typeof text == "string" && (this.fillStyle != "#f5f6ce" && this.fillStyle !== "#444444")) {
                // Dirty fix for the missing icon in the initial welcome messages
                if (text == '') {
                    text = '📢';
                }
                lastChatNickname = text;
                lastChatNicknameColor = this.fillStyle;
            }
            if (typeof text == "string" && (this.fillStyle == "#f5f6ce" || this.fillStyle == "#444444")) {
                // Unfortunately chat messages will be printed more than just once and I don't know
                // how to identifiy them, so for now all messages will be stored and only new messages will be shown.
                // Ofcourse this means messages won't be shown if they are sned more than once (by the same nickname).
                var found = false;
                for (var i = 0; i < chatLog.length; i++) {
                    if (chatLog[i].nickname == lastChatNickname && chatLog[i].nicknameColor == lastChatNicknameColor && chatLog[i].message == text) {
                        found = true;
                        break;
                    };
                }

                if (! found) {
                    // NOTE: We might have to look for the coordinates of the text to find out the order of the messages (somehow)
                    chatLogCode += '<div><span class="time">' + (new Date().toLocaleTimeString()) + '</span> <span class="nickname" style="color: ' + lastChatNicknameColor + '">' + htmlEntities(lastChatNickname) + '</span>';
                    chatLogCode += '<span class="message" style="color: #f5f6ce">' + htmlEntities(text) + '</span></div>';
                    chatLog.push({nickname: lastChatNickname, nicknameColor: lastChatNicknameColor, message: text});
                }
            }
        }

        return originalFillText.apply(this, arguments);
    }

    var performSearch = function(searchElement) {
        var subject = searchElement.value.toLowerCase();

        $('#miracle-complete-chatlog div').each(function(a,b,c) {
            var $entry = $(this);

            if ($entry.text().toLowerCase().indexOf(subject) == -1 && subject != '') {
                $entry.hide()
            } else {
                $entry.show()
            }
        });
    }

    var $modal = $('<div id="miracle-chatlog" class="miracle-primary-color-font" style="position: fixed; width: 100%; height: 100%; padding: 50px; color: #FF69B4; background-color: rgba(0,0,0,0.95); user-select: text; overflow-y: auto; z-index: 999; display: none"></div>');
    $modal.append('<style>#miracle-complete-chatlog div:hover { background-color: rgba(255,255,255,0.1) } #miracle-complete-chatlog .time { padding-right: 10px; color: #666; }</style>');
    $modal.append('<h1>Complete chatlog</h1><br>');
    $modal.append('<div id="miracle-complete-chatlog"></div>');
    $modal.append($('<input type="text" style="display: inline-block; position: fixed; right: 20px; bottom: 20px;" placeholder="Type to search">').keyup(function(event) { performSearch(this) }));
    $modal.append($('<br><a href="#" class="miracle-primary-color-background" style="display: inline-block; margin-top: 20px; padding: 10px; color: white">Close</a>').click(function() { $modal.hide() }));
    $('body').append($modal);

    $('#miracle-complete-chatlog').dblclick(function() {
        var $clickTarget = null;

        // Each chat message is a div with spans in it. Either the spans or the div might be clicked.
        if (event.target.tagName.toLowerCase() == 'span') {
            $clickTarget = $(event.target).parent();
        } else {
            $clickTarget = $(event.target);
        }

        var message = $clickTarget.find('.message').text();

        // Messages usually start with ': ' but we do not want to "translate" it, so we remove it
        if (message.substr(0, 2) == ': ') {
            message = message.substr(2);
        }

        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        window.open('https://www.deepl.com/translator#en/' + settings.targetLanguage + '/' + message);
    });

    window.addEventListener('keyup', function()
                            {
        // Ignore text input field so typing in them is possible
        if (document.activeElement.type == 'text' || document.activeElement.type == 'password') {
            return;
        }

        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        if (event.keyCode == settings.bindings.chatLog) {
            $('#miracle-complete-chatlog').html(chatLogCode);
            $modal.toggle();
            $modal.get(0).scrollTo(0,$modal.get(0).scrollHeight);
        }
    });
})();


(function() {
    'use strict';

    // We need to have a delay, because the menu is not loaded right away
    setTimeout(function() {
        var settings = JSON.parse(localStorage.getItem('miracleScripts'));

        var favIconClick = function() {
            var id = parseInt($(this).parent().parent().find('button').attr('onclick').substr(11));

            if (settings.favSkins.includes(id)) {
                $(this).addClass('skin-not-fav');
                $('#skinUseBtn' + id).parent().find('span').addClass('skin-not-fav');
                var index = settings.favSkins.indexOf(id);
                settings.favSkins.splice(index, 1);
            } else {
                $(this).removeClass('skin-not-fav');
                settings.favSkins.push(id);
            }
            localStorage.setItem('miracleScripts', JSON.stringify(settings));
            renderFavSkins();
        };

        var renderFavSkins = function() {
            var $skins = null;

            if ($('#fav-skins').length > 0) {
                $skins = $('#fav-skins');
                $skins.html('');
            } else {
                $skins = $('<div id="fav-skins" style="background-color: #4d4950"></div>');
                $skins.insertAfter('#publicSkinsHeader');

                $('#fav-skins').click(function(event) {
                    if (event.target.tagName.toLowerCase() == 'span') {
                        favIconClick.apply(event.target);
                    }
                });
            }
            settings.favSkins.forEach(function(id) {
                $skins.append('<div style="float: left; padding: 5px;"><img style="border: 1px solid rgba(0,0,0,.25); border-radius: 50%; box-shadow: 0 0 2px #000;" src="skins/' + id + '_lo.png?u=1575650762" alt=""><h4><span style="cursor: pointer">⭐</span></h4><button class="btn btn-primary skinuse-btn" onclick="toggleSkin(' + id + ');">Use</button></div>');
            });
            $skins.append('<div style="clear: both"></div>');
        };

        var addFavIcons = function() {
            var $skins = $('#publicSkinsPage');
            $skins.append('<style>.skin-not-fav { opacity: 0.3 }</style>');

            $skins.find('h4').each(function() {
                var $favIcon = $('<span style="cursor: pointer">⭐</span>');
                var id = parseInt($(this).parent().find('button').attr('onclick').substr(11));

                $favIcon.click(favIconClick);

                $(this).append($favIcon);

                if (! settings.favSkins.includes(id)) {
                    $favIcon.addClass('skin-not-fav');
                }
            });
        }
        var initialized = false;
        $('#skinsCustomTab, #skinExampleMenu').click(function() {
            if (! initialized) {
                var checkState = function() {
                    if ($('#publicSkinsPage').html() != '') {
                        addFavIcons();
                        renderFavSkins();
                    } else {
                        setTimeout(checkState, 30);
                    }
                };
                checkState();
                initialized = true;
            }
        });
        $('#phpSkins').click(function(event) {
            if (event.target.classList.contains('publicskins-nav-btn')) {
                addFavIcons();
            }
        });

    }, 500);
})();


(function() {
    'use strict';

    var settings = JSON.parse(localStorage.getItem('miracleScripts'));

    var sessionStartedAt = Date.now();

    var originalToggleSkin = window.toggleSkin;
    window.toggleSkin = function()
    {
        window.curserMessage('Picked skin with ID ' + arguments[0]);

        return originalToggleSkin.apply(this, arguments);
    };

    $('#chtbox').keydown(function(event)
                         {
        if (event.keyCode == 13) {
            var command = $('#chtbox').val();

            if (command == 'time' || command == '/time') {
                var now = new Date();
                $('#chtbox').val('Local time: '+ now.toLocaleString()).focus();
            }

            if (command == 'minutes' || command == '/minutes') {
                var minutes = parseInt((Date.now() - sessionStartedAt) / 1000 / 60);
                if (minutes > 60) {
                    minutes = '' + minutes;
                    minutes = parseInt(minutes / 60) + ' Hours & ' + (minutes % 60);
                }
                $('#chtbox').val('has played for: '+ minutes + ' Minutes in the current session').focus();
            }

            if (command == 'ping' || command == '/ping') {
                var pingRating = 'Extremly bad!', ping = $('#ping').text();
                if (parseInt(ping) > 0) {
                    if (parseInt(ping) < 40) pingRating = 'Perfect!';
                    if (parseInt(ping) < 70) pingRating = 'Good!';
                    if (parseInt(ping) < 110) pingRating = 'Acceptable!';
                    if (parseInt(ping) < 150) pingRating = 'Bad!';
                }
                else {
                    ping = '∞ (infinite) ';
                }
                $('#chtbox').val('has a ping of: ' + ping + '. ' + pingRating).focus();
            }
            if (command == 'fps' || command == '/fps') {
                var fpsRating = 'Perfect!', fps = $('#fps').text();
                if (fps < 10) fpsRating = 'Extremly bad!';
                if (fps < 30) fpsRating = 'Bad!';
                if (fps < 40) fpsRating = 'Acceptable!';
                if (fps < 57) fpsRating = 'Good!';

                $('#chtbox').val('has ' + fps + ' frames per second (fps). ' + fpsRating).focus();
            }

            var useSkin = function(skinSlot, skinId) {
                if (skinId) {
                    skinId = parseInt(skinId);
                    settings.quickSkins[skinSlot - 1] = skinId;
                    localStorage.setItem('miracleScripts', JSON.stringify(settings));
                } else {
                    skinId = settings.quickSkins[skinSlot - 1];
                    if (! skinId) {
                        window.curserMessage('Skin not set yet, set with /skin' + skinSlot + ' id');
                        $('#chtbox').val('').focus();
                        return;
                    }
                }

                window.azad(true);

                setTimeout(function() {
                    $('#skinExampleMenu').click();

                    var checkLoaded = function() {
                        var loaded = ($('#skinsFree tr').length > 1);
                        if (loaded) {
                            toggleSkin(skinId);

                            setTimeout(function(){
                                $('#shopModalDialog button.close').click();

                                setTimeout(function(){
                                    setNick(document.getElementById('nick').value);
                                }, 200);
                            }, 200);
                        } else {
                            setTimeout(checkLoaded, 300);
                        }
                    };
                    checkLoaded();
                }, 200);

                $('#chtbox').val('').focus();
            };
            if (command.substr(0, 5) == 'skin1' || command.substr(0, 6) == '/skin1') {
                useSkin(1, command.substr(6));
            }
            if (command.substr(0, 5) == 'skin2' || command.substr(0, 6) == '/skin2') {
                useSkin(2, command.substr(6));
            }
            if (command.substr(0, 5) == 'skin3' || command.substr(0, 6) == '/skin3') {
                useSkin(3, command.substr(6));
            }
            if (command.substr(0, 5) == 'skin4' || command.substr(0, 6) == '/skin4') {
                useSkin(4, command.substr(6));
            }
            if (command.substr(0, 5) == 'skin5' || command.substr(0, 6) == '/skin5') {
                useSkin(5, command.substr(6));
            }
        }
    });
})();


console.log('🌸 Miracle Scripts successfully loaded!');