IdleScape - Lootify

Addons for the IdleScape Fighting System

目前為 2021-01-24 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         IdleScape - Lootify
// @namespace    D4IS
// @version      1.2.3
// @description  Addons for the IdleScape Fighting System
// @author       D4M4G3X
// @match        http*://*idlescape.com/game
// @match        https://idlescape.com/game
// @match        https://www.idlescape.com/game
// @grant        none
// @require http://code.jquery.com/jquery-3.4.1.min.js
// @run-at document-start
// ==/UserScript==

(function() {
    'use strict';
    let ver = '1.2.3';
    let newver = getNewVer();
    let runs = [{}];
    let total = {};
    let totalLoot = {};
    let isSetup = false;
    let time = 0;
    let prices = getMarketPrices();
    let user = {};
    let headers = {};
        let timer = {
            'running': false,
            'starts': [],
            'stops': [],
            'interval': function() {
                let e = setInterval(()=> {
                    if(!isStatus('Fighting')) {
                        clearInterval(e);
                    }
                    if($('.time-stats').length) {
                        $('.time-stats').find('span').text('Elapsed: '+getTime());
                    }
                }, 10);
            },
            'update': setInterval(()=> {
                if($('.status-bar').length) {
                    if(isStatus('Fighting')) {
                        if (!timer.running) {
                            timer.running = true;
                            timer.starts.unshift(new Date());
                            timer.interval();
                        }
                    } else {
                        if(timer.running) {
                            timer.running = false;
                            timer.stops.unshift(new Date());
                        }
                    }
                }
            },500),
        }
    /* ############## CSS STYLES ############## */
    let styles = {};
    styles.boxshadow = {
        'webkit-box-shadow': '0 1px 3px rgba(0,0,0,.3),inset 0 1px 1px rgba(255,255,255,.2)',
        '-moz-box-shadow': '0 1px 3px rgba(0,0,0,.3),inset 0 1px 1px rgba(255,255,255,.2)',
        'box-shadow': '0 1px 3px rgba(0,0,0,.3),inset 0 1px 1px rgba(255,255,255,.2)',
    };
    styles.header = {
        'display': 'flex',
        'justify-content': 'space-between',
        'align-items': 'center',
        'border-radius': '8px',
        'padding': '4px 0 4px 10px',
        'background-color': '#2d2d2d',
    };
    styles.mobheader = Object.assign({}, styles.boxshadow, styles.header, {
        'margin': '4px auto',
    });
    styles.lootheader = Object.assign({}, styles.boxshadow, styles.header, {
        'margin': '0 auto',
        'border-bottom': '2px solid transparent'
    });
    styles.headerImage = Object.assign({}, styles.boxshadow, {
        'background-color': '#313b46',
        'border-radius': '100%',
        'padding': '4px',
        'height': '35px',
        'width': '35px',
    });
    styles.entry = {
        'border-radius': 'none',
        'padding': '4px 8px',
        'border-bottom': '1px dotted #2d2d2d',
    };
    styles.submenu = {
        'width': '80%',
        'margin': '0 auto',
        'padding': '5px 0',
    };
    styles.lootwrap = {
        'border-radius': '8px',
        'background-color': '#3c3c3c',
    };
    styles.setting = Object.assign({}, styles.boxshadow, {
        'width':'80%',
        'display':'flex',
        'margin':'4px auto',
        'padding': '4px 8px',
        'justify-content': 'space-between',
        'align-items': 'center',
        'background-color': '#3c3c3c',
        'border-radius': '8px',
    });
    styles.input = {
        'background-color': '#FFF',
        'margin': '0',
        'font-size': '14px',
    };
    styles.inputCheck = Object.assign({}, styles.input, {
        'margin': '0 25px',
        'opacity': '1',
        'position': 'relative',
        'pointer-events': 'auto',
        'transform': 'scale(1.5)',
    });
    styles.inputNumber = Object.assign({}, styles.input, {
        'height': '20px',
        'width': '50px',
        'border-radius': '10px',
        'padding': '0px 4px 0 10px',
    });
    /* ############## INTERVALS ############## */
    let mainInterval = setInterval(()=> {
        if ($('.status-bar').length) {
            if(!isSetup) {
                isSetup = true;
                updateUser();
                setupSettings();
                setupLogs();
                setupTimer();
                setupKPH();
                !isIron() ? setupGPH() : 1;
            }
            checkUpdates();
        }
        if (isStatus('Fighting')) {
            saveLogs();
            mergeLogs();
            renderLogs();
            renderKPH();
            !isIron() ? renderGPH() : 1;
        }
        if ($('.combat-zone').length) {
            $('.combat-zone').unbind().click(function() {
                let $zone = $(this);
                $.each(getLogMobs(), function(key, mob) {
                    if (!getZoneMobs($zone.text()).includes(mob)) {
                        submitStats(getLogs(), getTH());
                        clearLogs();
                    }
                });
            });
        }
        cleanChat();
    }, 1000);
    let pasteInterval = setInterval(()=> {
        if (isStatus('Fighting')) {
            if(window.localStorage.getItem(getUsername() + '-AutoPaster') == 'true') {
                submitStats(getLogs(), getTH());
                clearLogs('single');
            }
        }
    }, getPasteInterval() * 60 * 1000);
    let adInterval = setInterval(()=> {
        chat({
            'msg': 'Lootify - Please report bugs to D4M4G3X#6263 on Discord!',
            'color': '#00a0fd',
        });
    }, 15 * 60 * 1000);
    let updateInterval = setInterval(()=> {
        prices = getMarketPrices();
        newver = getNewVer();
        updateUser();
    }, 5 * 60 * 1000);
    /* ############## SETUP AND RENDER ############## */
    function setupSettings() {
        let $lootBtn;
        if ($('.btn-loot-log').length) {
            $('.btn-loot-log').remove();
        }
        if (getMenuItem('Loot Log')) {
            editMenuItem('Loot Log');
        }
        if(!getMenuItem('Lootify', 'category')) {
            let $ltfHeader = setMenuItem({
                'text': 'Lootify',
                'clone': 'Gathering',
                'before': getMenuItem('Gathering', 'category'),
            }, 'category');
            $ltfHeader.append($('<i/>').text(' (v'+ver+')').css('font-size','12px').insertAfter($ltfHeader));
        }
        if(!getMenuItem('Lootify')) {
            $lootBtn = setMenuItem({
                'icon': '/images/money_icon.png',
                'text': 'Lootify Settings',
                'class': 'btn-lootify',
                'after': getMenuItem('Lootify', 'category'),
                'click': function() {
                    $('.log-paster-settings').toggle();
                },
            });
        } else {
            $lootBtn = $('.btn-lootify');
        }
        if (!$('.log-paster-settings').length) {
            let $wrap = $('<div/>', {
                'class': 'log-paster-settings'
            }).hide();
            $lootBtn.after($wrap);

            addSetting({
                'text': 'Enable Auto Paster',
                'name': 'AutoPaster',
                'type': 'checkbox',
                'default': 'false',
                'change': function() {
                    window.localStorage.setItem(getUsername() + '-AutoPaster', $(this).prop('checked'));
                }
            }).appendTo($wrap);
            addSetting({
                'text': 'Paste Interval (minutes)',
                'name': 'AutoPasterInterval',
                'type': 'number',
                'min': 15,
                'max': 60,
                'default': 30,
                'change': function() {
                    if($(this).val() >= $(this).attr('min') && $(this).val() <= $(this).attr('max')) {
                        window.localStorage.setItem(getUsername() + '-AutoPasterInterval', $(this).val());
                    }
                }
            }).appendTo($wrap);
        }
    }
    function setupLogs() {
        if(!getMenuItem('Loot Log')) {
            setMenuItem({
                'icon': '/images/ui/inventory_icon.png',
                'text': 'Loot Log',
                'class': 'btn-loot-log',
                'before': getMenuItem('Gathering', 'category'),
                'click': function() {
                    $('.item-log-clone').toggle();
                },
            });
        }
        let $logwrap = $('.item-log-window');
        $logwrap.hide();
        let $clone = $logwrap.clone();
        $clone.addClass('item-log-clone').removeClass('hidden');
        $clone.css(styles.submenu);
        $clone.find('.item-log-timer').remove();
        $clone.find('.item-log-info').remove();
        $clone.find('.drawer-setting-large').addClass('clone').unbind().click(function() {
            submitStats(getLogs(), getTH());
            clearLogs();
        }).text('Paste and Reset Log');
        getMenuItem('Loot Log').after($clone);
    }
    function setupTimer() {
        if (!getMenuItem('Elapsed:')) {
            let $timeStats = setMenuItem({
                'class': 'time-stats',
                'text': 'Elapsed: 0S',
                'icon': '/images/clock.png',
                'after': $('.log-paster-settings'),
                'css': {
                    'font-size': '14px',
                }
            });
        }
    }
    function setupKPH() {
        if (!getMenuItem('Kills:')) {
            let $kphStats = setMenuItem({
                'class': 'kph-stats',
                'text': 'Kills: 0 p/h',
                'icon': '/images/combat/combat_level.png',
                'after': $('.time-stats'),
                'click': function() {
                    $('.kph-wrap').toggle();
                },
                'css': {
                    'font-size': '14px',
                },
            });
            $kphStats.after($('<div/>', {
                'class': 'kph-wrap'
            }).css(styles.submenu).hide());
        }
    }
    function renderKPH() {
        let mobkills = {};
        $.each(total, function(mob, mobinfo) {
            mobkills[mob] = mobinfo.count;
        });
        let totalkills = function() {
            let c = 0;
            $.each(mobkills, function(mob, kills) {
                c += kills;
            });
            return c;
        };
        $('.kph-stats').find('span').text('Kills: ' + addCommas(Math.floor((totalkills()/time)*3600)) + ' p/h');
        $('.kph-wrap').empty();
        $.each(sortObject(mobkills), function(mob, count) {
            let $mobwrap = $('<div/>').css(styles.mobheader).appendTo($('.kph-wrap'));
            $mobwrap.append($('<span/>').css('display','block').text(mob + ': ' + Math.floor((count/time)*3600) + ' p/h'));
        });
    }
    function setupGPH() {
        if (!getMenuItem('Gold:')) {
            let $gphStats = setMenuItem({
                'class': 'gph-stats',
                'text': 'Loot value: 0 p/h',
                'icon': '/images/ui/shop_icon.png',
                'after': $('.kph-wrap'),
                'click': function() {
                    $('.gph-wrap').toggle();
                },
                'css': {
                    'font-size': '14px',
                }
            });
            $gphStats.after($('<div/>', {
                'class': 'gph-wrap'
            }).css(styles.submenu).hide());
        }
    }
    function renderGPH() {
        let totalGold = 0;
        $.each(totalLoot, function(item, count) {
            if (item === 'Gold') {
                totalGold += count;
            } else {
                totalGold += prices[item] * count;
            }
        });
        $('.gph-stats').find('span').text('Gold: ' + addCommas(Math.floor((totalGold/time)*3600)) + ' p/h');
        $('.gph-wrap').empty();
        let $lootwrap = $('<div/>').css(styles.mobheader).appendTo($('.gph-wrap'));
        $lootwrap.append($('<span/>').css('display','block').text('Loot value: ' + addCommas(Math.floor(totalGold))));
    }
    function saveLogs() {
        let $logcats = $('.item-log-window:not(.item-log-clone)').find('.item-log-cateogry');
        if ($logcats.length) {
            $logcats.each(function() {
                if( $(this).find('.item-log-category-closed').length ) {
                    $(this).find('.item-log-category-closed').click();
                }
                let mobs = $(this).find('.item-log-category-open').text().split(" x ");
                !runs[0][mobs[0]] ? runs[0][mobs[0]] = {} : 1;
                runs[0][mobs[0]].count = parseInt(mobs[1]);
                $(this).find('.item-log-item').each(function() {
                    if( $(this).text() !== "None" ) {
                        let loot = $(this).text().split(" x ");
                        !runs[0][mobs[0]].loot ? runs[0][mobs[0]].loot = {} : 1;
                        !runs[0][mobs[0]].loot[loot[0]] ? runs[0][mobs[0]].loot[loot[0]] = {} : 1;
                        runs[0][mobs[0]].loot[loot[0]].count = parseInt(loot[1]);
                    }
                });
            });
        }
    }
    function mergeLogs() {
        if(runs) {
            total = {};
            totalLoot = {};
            $.each(runs, function(num, run) {
                $.each(run, function(mobname, mobinfo) {
                    !total[mobname] ? total[mobname] = {} : 1;
                    !total[mobname].count ? total[mobname].count = 0 : 1;
                    total[mobname].count += mobinfo.count;
                    $.each(mobinfo.loot, function(lootname, lootinfo) {
                        /* SET TOTAL LOOT PER MOB */
                        !total[mobname].loot ? total[mobname].loot = {} : 1;
                        !total[mobname].loot[lootname] ? total[mobname].loot[lootname] = {} : 1;
                        !total[mobname].loot[lootname].count ? total[mobname].loot[lootname].count = 0 : 1;
                        total[mobname].loot[lootname].count += lootinfo.count;
                        /* SET TOTAL LOOT OVERALL */
                        !totalLoot[lootname] ? totalLoot[lootname] = 0 : 1;
                        totalLoot[lootname] += lootinfo.count;
                    });
                });
            });
            delete total[''];
        }
    }
    function renderLogs() {
        if(!runs) { return; }
        let $cat, $mobwrap, $mobheader, $lootentry;
        $('.item-log-clone').find('.item-log-cateogry').remove();
        $.each(sortObject(total), function(mob, mobinfo) {
            $cat = $('<div/>', {
                'class':'item-log-cateogry noselect'
            }).css(styles.lootwrap);
            $mobwrap = $('<div/>').css(styles.lootheader).hover(function() {
                $(this).css('border-bottom', '2px solid #888');
            }, function() {
                $(this).css('border-bottom', '2px solid transparent');
            }).appendTo($cat);
            $cat.click(function() {
                headers[mob] = headers[mob] == false ? true : false;
                $mobwrap.find('.loot-entry').toggle();
            });
            $mobwrap.append($('<div/>').text(mob + ' x ' + mobinfo.count));
            $mobwrap.append($('<img/>').css(styles.headerImage).addClass('drawer-item-icon').attr('src', getMobIcon(mob)));
            if(mobinfo.loot) {
                $.each(sortObject(mobinfo.loot), function(loot, lootinfo) {
                    $lootentry = $('<div/>', {
                        'class': 'loot-entry'
                    }).css(styles.entry).text(loot + ' x ' + lootinfo.count).appendTo($cat);
                    headers[mob] = headers[mob] ? headers[mob] : true;
                    if (!headers[mob]) {
                        $lootentry.hide();
                    }
                });
            } else {
                $lootentry = $('<div/>', {
                    'class': 'loot-entry'
                }).css(styles.entry).html('<i>None</i>').appendTo($cat);
                headers[mob] = headers[mob] ? headers[mob] : false;
                if (!headers[mob]) {
                    $lootentry.hide();
                }
            }
            $('.drawer-setting-large.clone').before($cat);
        });
    }
    /* ############## ACTIONS ############## */
    function getLogs() {
        let logs = [];
        let $logcats = $('.item-log-window:not(.item-log-clone)').find('.item-log-cateogry');
        if ($logcats.length) {
            $logcats.each(function(k,v) {
                if( $(this).find('.item-log-category-closed').length ) {
                    $(this).find('.item-log-category-closed').click();
                }
                if( $(this).find('.item-log-category-open').length ) {
                    logs.push($(this).find('.item-log-category-open').text());
                    $(this).find('.item-log-item').each(function() {
                        if( $(this).text() !== "None" ) {
                            logs.push($(this).text());
                        }
                    });
                }
            });
        }
        return logs;
    }
    function clearLogs(m = 'all') {
        if (m == 'single') {
            runs.unshift({});
        } else if (m == 'all') {
            time = 0;
            runs = [{}];
            total = {};
            isSetup = false;
            $('.item-log-clone').remove();
        }
        $('.item-log-window').find('.drawer-setting-large.active:not(.clone)').click();
    }
    /* ############## GAME LIBRARY ############## */
    /* === FILTERS === */
    function isIron() {
        return $('.header-league-icon').attr('src') === '/images/leagues/ironman_league_icon_v5.png';
    }
    function isStatus(txt) {
        return $('.status-bar').length && ~$('.status-bar').text().indexOf(txt);
    }
    /* === GET DATA === */
    function getUsername() {
        return $('.navbar1-box').text().split(' ')[1];
    }
    function getTH() {
        return getEnchantment('32');
    }
    function getEnchantment(id) {
        let TH = 0;
        if(user.enchantments) {
            $.each(user.enchantments, function(k, v) {
                if(v.enchantmentID == id) {
                    TH = v.enchantmentStrength;
                }
            });
        }
        return TH;
    }
    function getLogMobs() {
        let mobs = [];
        $.each(total, function(mob, info) {
            mobs.push(mob);
        });
        return mobs;
    }
    function getZoneMobs(zone) {
        let mobs = [];
        switch (zone) {
            case 'Farm':
                mobs = ['Cow', 'Chicken', 'Small Rat'];
                break;
            case 'Caves':
                mobs = ['Imp', 'Greater Imp'];
                break;
            case 'City':
                mobs = ['Guard', 'Black Knight'];
                break;
            case 'Lava Maze':
                mobs = ['Deadly Red Spider', 'Lesser Demon'];
                break;
            case 'Valley of Giants':
                mobs = ['Fire Giant', 'Moss Giant', 'Ice Giant'];
                break;
        }
        return mobs;
    }
    function getPasteInterval() {
        let interval = parseInt($('.AutoPasterInterval').val());
        interval = interval ? interval : 30;
        interval = interval < 15 ? 15 : interval;
        interval = interval > 60 ? 60 : interval;
        return interval;
    }
    function getMarketPrices() {
        let p = {};
        $.getJSON( "https://api.idlescape.xyz/prices", function( data ) {
            if(data) {
                $.each(data.items, function(k, v) {
                    p[v.name] = v.price;
                });
            } else {
                p = false;
            }
        });
        return p;
    }
    function getMobIcon(name) {
        let link = 'https://idlescape.com/images/combat/monsters/';
        switch (name) {
            case 'Giant Rat':
                link += 'rat.png';
                break;
            case 'Chicken':
                link += 'chicken.png';
                break;
            case 'Cow':
                link += 'cow.png';
                break;
            case 'Goblin':
                link += 'goblin.png';
                break;
            case 'Imp':
            case 'Greater Imp':
                link += 'imp.png';
                break;
            case 'Guard':
                link += 'guard.svg';
                break;
            case 'Black Knight':
                link += 'black_knight.png';
                break;
            case 'Deadly Red Spider':
                link += 'deadly_red_spider.png';
                break;
            case 'Lesser Demon':
                link += 'lesser_demon_no_highlight.png';
                break;
            case 'Spriggan':
                link += 'spriggan.png';
                break;
            case 'Greater Demon':
                link += 'greater-demon.png';
                break;
            case 'Fire Giant':
                link += 'fire_giant.png';
                break;
            case 'Moss Giant':
                link += 'moss_giant.png';
                break;
            case 'Ice Giant':
                link += 'ice_giant.png';
                break;
            case 'Abberant Shrimp':
                link += 'shrimp_abberation.png';
                break;
        }
        return link;
    }
    /* === ACTIONS === */
    function chat(args) {
        let e = setInterval(()=> {
            let $chat = $('.chat-message-container > .chat-message-list > div');
            if( $chat.length ) {
                clearInterval(e);
                $chat.each(function() {
                    if (!$(this).find('.activity-log').length) {
                        let $msg = $('<div/>', {
                            'class': 'chat-message msg-lootify'
                        }).clone().appendTo($(this));
                        if (args['date'] !== false) {
                            let $date = $('<span/>', {
                                'class': 'message-time-stamp',
                            }).text('['+getDate(new Date)+']').appendTo($msg);
                        }
                        args['color'] = args['color'] ? args['color'] : '#00A0FD';
                        args['glow'] = args['glow'] ? '0 0 3px '+args['glow'] : 'none';
                        let $txt = $('<span/>', {
                            'class':'chat-message-system'
                        }).css({
                            'font-size': '14px',
                            'color': args['color'],
                            'text-shadow': args['glow'],
                        }).text(args['msg']).appendTo($msg);

                        args['ttl'] = args['ttl'] ? args['ttl'] : 5;
                        setTimeout(()=> {
                            $msg.remove();
                        }, args['ttl'] * 60 * 1000);
                    }
                });
            }
        }, 1000);
    }
    function cleanChat() {
        let $chat = $('.chat-message-container');
        if( $chat.length ) {
            $chat.each(function() {
                if($(this).find('.activity-log').length) {
                    $(this).find('.msg-lootify').remove();
                }
            });
        }
    }
    function checkUpdates() {
        if (ver < newver) {
            if (!$('.lootify-update').length) {
                $('<div/>', {
                    'class': 'lootify-update'
                }).css({
                    'color': 'red',
                    'width': '80%',
                    'margin': '0 auto',
                }).text('Update: v'+newver+' available!').insertBefore(getMenuItem('Lootify Settings'));
            }
        }
    }
    function setNewVer(v) {
        newver = v.toString();
    }
    function getNewVer() {
        $.get('https://digimol.net/lootify/api.php?a=ver').done(function(data) {
            setNewVer(data);
        });
    }
    function updateUser() {
        $.get('https://digimol.net/lootify/api.php?a=updateuser&id='+user.id+'&name='+user.name+'&v='+ver);
    }
    function submitStats(logs, th) {
        if (logs.length < 1) {
            return false;
        }
        let targetUrl = 'https://docs.google.com/forms/u/0/d/e/1FAIpQLSch3eG9Tqts0tIvnkk-C5JZeTwfbkWXhxkIpFnxyyaNO26h4Q/formResponse';
        let logEntryId = 'entry.558332813';
        let thEntryId = 'entry.22586929';
        let noteEntryId = 'entry.1726819066';
        let finalTH = (parseInt(th) > 0) ? 'TH '+th : 'None';
        let fullUrl = targetUrl + '?'+ noteEntryId + '=Lootify-' + ver + '&' + thEntryId + '=' + encodeURIComponent(finalTH) + '&' + logEntryId + '=' + encodeURIComponent(logs.join('\n'));
        $.get(fullUrl);
        $.get('https://digimol.net/lootify/api.php?a=paste&th='+th+'&user='+user.id+'&log='+encodeURIComponent(JSON.stringify(runs[0])));
        chat({
            'msg': 'Lootify - Loot log pasted!',
            'color': '#00a0fd',
        });
    }
    function setMenuItem(args, type = 'item') {
        let $item, $img, e;
        args['text'] = args['text'] ? args['text'] : 'Menu item';
        args['clone'] = args['clone'] ? args['clone'] : 'Shops';
        $item = getMenuItem(args['clone'], type).clone();
        if(args['class']) {
            $item.addClass(args['class']);
        }
        if(args['css']) {
            $item.css(args['css']);
        }
        $img = $item.find('img').clone();
        $item.unbind().empty();
        if(type === 'item') {
            $item.append($('<span/>').text(args['text']));
        } else if (type === 'category') {
            $item.append($('<b/>').text(args['text']));
        }
        if (args['icon']) {
            $img.attr('src', args['icon']).prependTo($item);
        }
        args['before'] ? args['before'].before($item) : 1;
        args['after'] ? args['after'].after($item) : 1;
        if (typeof args['click'] === 'function') {
            $item.click(args['click']);
        }
        return $item;
    }
    function getMenuItem(txt, type='item') {
        let $item;
        $.each($('.drawer-' + type), function() {
            if (~$(this).text().indexOf(txt)) {
                $item = $(this);
            }
        });
        return $item;
    }
    function editMenuItem(txt) {
        getMenuItem(txt).text('Hidden').hide();
    }
    function removeMenuItem(txt) {
        getMenuItem(txt).remove();
    }
    function addSetting(args) {
        args['text'] = args['text'] ? args['text'] : 'New setting';
        args['name'] = args['name'] ? args['name'] : args['text'].replace(' ', '-').toLowerCase();
        args['type'] = args['type'] ? args['type'] : 'text';
        args['default'] = args['default'] ? args['default'] : 0;
        let $setting = $('<div/>').css(styles.setting).append($('<span/>')).append($('<input/>'));;
        $setting.find('span').text(args['text']);
        $setting.find('input').addClass(args['name']).attr('type', args['type']);
        switch(args['type']) {
            case 'checkbox':
                $setting.find('input').css(styles.inputCheck);
                break;
            case 'number':
                $setting.find('input').css(styles.inputNumber);
                break;
        }
        if (args['min']) {
            $setting.find('input').attr('min', args['min']);
        }
        if (args['max']) {
            $setting.find('input').attr('max', args['max']);
        }
        let val = window.localStorage.getItem(getUsername() + '-' + args['name']);
        if (args['type'] === 'checkbox') {
            val = val ? val : args['default'];
            val = val == 'true' ? true : false;
            $setting.find('input').prop('checked', val);
        }
        if (val) {
            $setting.find('input').val(val)
        } else {
            $setting.find('input').val(args['default']);
        }
        if (args['change']) {
            $setting.find('input').change(args['change']);
        }
        return $setting;
    }
    /* ############## SOCKET SETUP ############## */
    const sockets = [];
    const nativeWebSocket = window.WebSocket;
    window.WebSocket = function(...args){
        const socket = new nativeWebSocket(...args);
        sockets.push(socket);
        return socket;
    };
    let setupSocket = setInterval(()=> {
        if(sockets.length != 0){
            clearInterval(setupSocket);
            sockets[0].addEventListener('message', (e) => messageHandler(e));
        }
    }, 1000);
    function messageHandler(e) {
        let msg = e.data;
        msg = (msg.match(/^[0-9]+(\[.+)$/) || [])[1];
        if(msg && ~msg.indexOf('"update player"')) {
            let data = JSON.parse(msg.split('Socket'))[1];
            if(~msg.indexOf('activeEnchantments')) {
                if(~msg.indexOf('"portion":"all"')) {
                    user.enchantments = data.value.activeEnchantments;
                    user.id = data.value.id;
                    user.name = data.value.username;
                } else {
                    user.enchantments = data.value[0];
                }
            }
        }
    }
    /* ############## GENERAL LIBRARY ############## */
    function addCommas(nStr) {
        nStr += '';
        let x = nStr.split('.');
        let x1 = x[0];
        let x2 = x.length > 1 ? '.' + x[1] : '';
        let rgx = /(\d+)(\d{3})/;
        while (rgx.test(x1)) {
            x1 = x1.replace(rgx, '$1' + ',' + '$2');
        }
        return x1 + x2;
    }
    function getDate(date) {
        let h = date.getHours();
        let m = date.getMinutes();
        let s = date.getSeconds()
        m = m < 10 ? '0'+m : m;
        s = s < 10 ? '0'+s : s;
        let strTime = h + ':' + m + ':' + s;
        if(~$('.chat-message .message-time-stamp').text().indexOf(' AM]') || ~$('.chat-message .message-time-stamp').text().indexOf(' PM]')) {
            let ampm = h >= 12 ? 'PM' : 'AM';
            h = h % 12;
            h = h ? h : 12;
            strTime = h + ':' + m + ':' + s + ' '  + ampm
        }
        return strTime;
    }
    function getTime() {
        let count = 0;
        $.each(timer.starts, function(k, start) {
            let stop = !timer.stops[k] ? new Date() : timer.stops[k];
            count += +stop - +start;
        });
        let s = Math.floor((count /  1000)) % 60;
        let m = Math.floor((count / 60000)) % 60;
        let h = Math.floor((count / 3600000)) % 24;
        let d = Math.floor((count / 86400000)) % 7;
        let w = Math.floor((count / 604800000)) % 52;
        let y = Math.floor((count / 31557600000));
        let timeStr = '';
        timeStr += (y>0) ? y+'Y ' : '';
        timeStr += (w>0) ? w+'W ' : '';
        timeStr += (d>0) ? d+'D ' : '';
        timeStr += (h>0) ? h+'H ' : '';
        timeStr += (m>0) ? m+'M ' : '';
        timeStr += (s>0) ? s+'S ' : '';
        time = s;
        return timeStr;
    }
    function sortObject(o) {
        var sorted = {},
            key, a = [];
        for (key in o) {
            if (o.hasOwnProperty(key)) {
                a.push(key);
            }
        }
        a.sort();
        for (key = 0; key < a.length; key++) {
            sorted[a[key]] = o[a[key]];
        }
        return sorted;
    }
})();