IdleScape - Lootify

Addons for the IdleScape Fighting System

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

// ==UserScript==
// @name         IdleScape - Lootify
// @namespace    D4IS
// @version      1.2.5
// @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.5';
    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': [],
        'reset': function() {
            timer.running = false;
            timer.starts = [];
            timer.stops = [];
            time = 0;
        },
        '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',
        'font-weight': 'bold',
    };
    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.rareEntry = Object.assign({}, styles.entry, {
        'color': '#FF863C',
        'text-shadow': '0 0 3px #FF863C',
    });
    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'
                    }).text(loot + ' x ' + lootinfo.count).appendTo($cat);
                    if(isRare(loot)) {
                        $lootentry.css(styles.rareEntry);
                    } else {
                        $lootentry.css(styles.entry);
                    }
                    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') {
            timer.reset();
            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);
    }
    function isRare(txt) {
        let rare = ['Moss Maul', 'Mysterious Seed', 'Satchel', "King's Crown", 'Kalanahmatti'];
        return rare.includes(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 = (count /  1000);
        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;
    }
})();