I looked at some of the issues mentioned above and made some changes in this version of the script, let me know if it resolves them for you and I'll see about pushing it into the official repo:  /* 
 * Version 0.2.1
 * Made By Robin Kuiper
 * Changes in Version 0.2.1 by The Aaron
 * Skype: RobinKuiper.eu
 * Discord: Atheos#1095
 * Roll20: <a href="https://app.roll20.net/users/1226016/robin" rel="nofollow">https://app.roll20.net/users/1226016/robin</a>
 * Roll20 Thread: <a href="https://app.roll20.net/forum/post/6349145/script-combattracker" rel="nofollow">https://app.roll20.net/forum/post/6349145/script-combattracker</a>
 * Github: <a href="https://github.com/RobinKuiper/Roll20APIScripts" rel="nofollow">https://github.com/RobinKuiper/Roll20APIScripts</a>
 * Reddit: <a href="https://www.reddit.com/user/robinkuiper/" rel="nofollow">https://www.reddit.com/user/robinkuiper/</a>
 * Patreon: <a href="https://patreon.com/robinkuiper" rel="nofollow">https://patreon.com/robinkuiper</a>
 * Paypal.me: <a href="https://www.paypal.me/robinkuiper" rel="nofollow">https://www.paypal.me/robinkuiper</a>
*/
/* TODO
 *
 * Styling
 * More chat message options
 * Show menu with B shows always
 * Add icon if not StatusInfo (?)    (IF YES, remove conditions on statusmarker remove)
 * Edit Conditions
*/
/* globals StatusInfo, TokenMod */
var CombatTracker = CombatTracker || (function() {
    'use strict';
    let round = 1,
        timerObj,
        intervalHandle,
        rotationInterval,
        paused = false,
        observers = {
            tokenChange: []
        },
        extensions = {
            StatusInfo: false
        };
    // Styling for the chat responses.
    const styles = {
        reset: 'padding: 0; margin: 0;',
        menu:  'background-color: #fff; border: 1px solid #000; padding: 5px; border-radius: 5px;',
        button: 'background-color: #000; border: 1px solid #292929; border-radius: 3px; padding: 5px; color: #fff; text-align: center;',
        textButton: 'background-color: transparent; border: none; padding: 0; color: #000; text-decoration: underline',
        list: 'list-style: none;',
        float: {
            right: 'float: right;',
            left: 'float: left;'
        },
        overflow: 'overflow: hidden;',
        fullWidth: 'width: 100%;',
        underline: 'text-decoration: underline;',
        strikethrough: 'text-decoration: strikethrough'
    },
    script_name = 'CombatTracker',
    state_name = 'COMBATTRACKER',
    handleInput = (msg) => {
        if (msg.type != 'api') return;
        let args = msg.content.split(' ');
        let command = args.shift().substring(1);
        let extracommand = args.shift();
        if(command !== state[state_name].config.command) return;
        if(extracommand === 'next'){
            if(playerIsGM(msg.playerid) || msg.playerid === 'api'){
                NextTurn();
                return;
            }
            let turn = getCurrentTurn(),
                token = getObj('graphic', turn.id);
            if(token){
                let character = getObj('character', token.get('represents'));
                if((token.get('controlledby').split(',').includes(msg.playerid) || token.get('controlledby').split(',').includes('all')) ||
                    (character && (character.get('controlledby').split(',').includes(msg.playerid) || character.get('controlledby').split(',').includes(msg.playerid)))){
                        NextTurn();
                        // SHOW MENU
                    }
            }
        }
        // Below commands are only for GM's
        if(!playerIsGM(msg.playerid)) return;
        let name, duration, direction, message, condition;
        switch(extracommand){
            case 'help':
                sendHelpMenu();
            break;
            case 'reset':
                switch(args.shift()){
                    case 'conditions':
                        state[state_name].conditions = {};
                    break;
                    default:
                        state[state_name] = {};
                        setDefaults(true);
                        sendConfigMenu();
                    break;
                }
            break;
            case 'config':
                if(args[0] === 'timer'){
                    if(args[1]){
                        let setting = args[1].split('|');
                        let key = setting.shift();
                        let value = (setting[0] === 'true') ? true : (setting[0] === 'false') ? false : setting[0];
                        state[state_name].config.timer[key] = value;
                    }
                    sendConfigTimerMenu();
                }else if (args[0] === 'announcements'){
                    if(args[1]){
                        let setting = args[1].split('|');
                        let key = setting.shift();
                        let value = (setting[0] === 'true') ? true : (setting[0] === 'false') ? false : setting[0];
                        state[state_name].config.announcements[key] = value;
                    }
                    sendConfigAnnounceMenu();
                }else{
                    if(args[0]){
                        let setting = args.shift().split('|');
                        let key = setting.shift();
                        let value = (setting[0] === 'true') ? true : (setting[0] === 'false') ? false : setting[0];
                        state[state_name].config[key] = value;
                    }
                    sendConfigMenu();
                }                 
            break;
            case 'prev':
                PrevTurn();
            break;
            case 'start':
                startCombat(msg.selected);
                if(args.shift() === 'b') sendMenu();
            break;
            case 'stop':
                stopCombat();
                if(args.shift() === 'b') sendMenu();
            break;
            case 'st':
                stopTimer();
                if(args.shift() === 'b') sendMenu();
            break;
            case 'pt':
                pauseTimer();
                if(args.shift() === 'b') sendMenu();
            break;
            case 'conditions':
                sendConditionsMenu();
            break;
            case 'show': {
                if(!msg.selected || !msg.selected.length){
                    makeAndSendMenu('No tokens are selected.', '', 'gm');
                    return;
                }
                let tokens = msg.selected.map(s => getObj('graphic', s._id));
                sendTokenConditionMenu(tokens);
			}
            break;
            case 'add':
                name = args.shift();
                if(!name){
                    makeAndSendMenu('No condition name was given.', '', 'gm');
                    return;
                }
                duration = args.shift();
                duration = (!duration || duration === 0) ? 'none' : duration;
                direction = args.shift() || -1;
                message = args.join(' ');
                condition = { name, duration, direction, message };
                if(!msg.selected || !msg.selected.length){
                    let tokenid = args.shift();
                    let token = getObj('graphic', tokenid);
                    if(!tokenid || !token){
                        makeAndSendMenu('No tokens were selected.', '', 'gm');
                        return;
                    }
                    addCondition(token, condition, true);
                    
                    return;
                }
                msg.selected.forEach(s => {
                    let token = getObj(s._type, s._id);
                    if(!token) return;
                    addCondition(token, condition, true);
                });
            break;
            case 'addfav':
                name= args.shift();
                duration = args.shift();
                direction = args.shift() || -1;
                message = args.join(' ');
                condition = { name, duration, direction, message };
                addOrEditFavoriteCondition(condition);
                sendFavoritesMenu();
            break;
            case 'editfav':
                name = args.shift();
                if(!name){
                    makeAndSendMenu('No condition name was given.', '', 'gm');
                    return;
                }
                name = strip(name).toLowerCase();
                condition = state[state_name].favorites[name];
                if(!condition){
                    makeAndSendMenu('Condition does not exists.', '', 'gm');
                    return;
                }                
                if(args[0]){
                    let setting = args.shift().split('|');
                    let key = setting.shift();
                    let value = (setting[0] === 'true') ? true : (setting[0] === 'false') ? false : setting[0];
                    state[state_name].favorites[name][key] = value;
                    if(key === 'name'){
                        state[state_name].favorites[strip(value).toLowerCase()] = state[state_name].favorites[name];
                        delete state[state_name].favorites[name];
                    }
                }
                sendEditFavoriteConditionMenu(condition);
            break;
            case 'removefav':
                removeFavoriteCondition(args.shift());
                sendFavoritesMenu();
            break;
            case 'favorites':
                sendFavoritesMenu();
            break;
            case 'remove': {
                let cname = args.shift();
                let tokenid = args.shift();
                let token;
                if(!cname){
                    makeAndSendMenu('No condition was given.', '', 'gm');
                    return;
                }
                if(tokenid){
                    token = getObj('graphic', tokenid);
                    if(token){
                        removeCondition(token, cname);
                        sendTokenConditionMenu([token]);
                        return;
                    }
                }
                if(!msg.selected || !msg.selected.length){
                    makeAndSendMenu('No tokens were selected.', '', 'gm');
                    return;
                }
                msg.selected.forEach(s => {
                    token = getObj(s._type, s._id);
                    if(!token) return;
                    removeCondition(token, cname);
                });
			}
            break;
            default:
                sendMenu();
            break;
        }
    },
    addOrEditFavoriteCondition = (condition) => {
        if(condition.duration === 0 || condition.duration === '') condition.duration = undefined;
        let strippedName = strip(condition.name).toLowerCase();
        state[state_name].favorites[strippedName] = condition;
    },
    removeFavoriteCondition = (name) => {
        name = strip(name).toLowerCase();
        delete state[state_name].favorites[name];
    },
    addCondition = (token, condition, announce=false) => {
        if(extensions.StatusInfo){
            const duration = condition.duration;
            const direction = condition.direction;
            const message = condition.message;
            condition = StatusInfo.getConditionByName(condition.name) || condition;
            condition.duration = duration;
            condition.direction = direction;
            condition.message = message;
        }
        if(!condition.duration || condition.duration === 0 || condition.duration === '0' || condition.duration === '' || condition.duration === 'none') condition.duration = undefined;
        if(state[state_name].conditions[strip(token.get('name')).toLowerCase()]){
            let hasCondition = false;
            state[state_name].conditions[strip(token.get('name')).toLowerCase()].forEach(c => {
                if(c.name.toLowerCase() === condition.name.toLowerCase()) hasCondition = true;
            });
            if(hasCondition) return;
            state[state_name].conditions[strip(token.get('name')).toLowerCase()].push(condition);
        }else{
            state[state_name].conditions[strip(token.get('name')).toLowerCase()] = [condition];
        }
        if(condition.icon){
//            let prevSM = token.get('statusmarkers');
            token.set('status_'+condition.icon, true);
            if(announce && extensions.StatusInfo){
                StatusInfo.sendConditionToChat(condition);
            }
        }else makeAndSendMenu('Condition ' + condition.name + ' added to ' + token.get('name'));
    },
    removeCondition = (token, condition_name, auto=false) => {
        if(!state[state_name].conditions[strip(token.get('name')).toLowerCase()]) return;
        let si_condition = false;
        if(extensions.StatusInfo){
            si_condition = StatusInfo.getConditionByName(condition_name) || false;
        }
        state[state_name].conditions[strip(token.get('name')).toLowerCase()].forEach((condition, i) => {
            if(condition.name.toLowerCase() !== condition_name.toLowerCase()) return;
            state[state_name].conditions[strip(token.get('name')).toLowerCase()].splice(i, 1);
            if(si_condition){
                token.set('status_'+condition.icon, false);
            }else if(!auto){
                makeAndSendMenu('Condition ' + condition.name + ' removed from ' + token.get('name'));
            }
        });
    },
    strip = (str) => {
        return str.replace(/[^a-zA-Z0-9]+/g, '_');
    },
    handleTurnorderChange = (obj, prev) => {
        if(obj.get('turnorder') === prev.turnorder) return;
        let turnorder = (obj.get('turnorder') === "") ? [] : JSON.parse(obj.get('turnorder'));
        let prevTurnorder = (prev.turnorder === "") ? [] : JSON.parse(prev.turnorder);
        if(obj.get('turnorder') === "[]"){
            resetMarker();
            stopTimer();
            return;
        }
        if(turnorder.length && prevTurnorder.length && turnorder[0].id !== prevTurnorder[0].id){
            doTurnorderChange();
        }
    },
    handleStatusMarkerChange = (obj, prev) => {
        if(extensions.StatusInfo){
            prev.statusmarkers = (typeof prev.get === 'function') ? prev.get('statusmarkers') : prev.statusmarkers;
            if(obj.get('statusmarkers') !== prev.statusmarkers){
                let nS = obj.get('statusmarkers').split(','),
                    oS = prev.statusmarkers.split(',');
                // Marker added?
                array_diff(oS, nS).forEach(icon => {
                    if(icon === '') return;
                    getObjects(StatusInfo.getConditions(), 'icon', icon).forEach(condition => {
                        addCondition(obj, { name: condition.name });
                    });
                });
                // Marker Removed?
                array_diff(nS, oS).forEach(icon => {
                    if(icon === '') return;
                    getObjects(StatusInfo.getConditions(), 'icon', icon).forEach(condition => {
                        removeCondition(obj, condition.name);
                    });
                });
            }
        }
    },
    handleGraphicMovement = (obj /*, prev */) => {
        if(!inFight()) return;
        if(getCurrentTurn().id === obj.get('id')){
            changeMarker(obj);
        }
    },
    array_diff = (a, b) => {
        return b.filter(function(i) {return a.indexOf(i) < 0;});
    },
    //return an array of objects according to key, value, or key and value matching
    getObjects = (obj, key, val) => {
        var objects = [];
        for (var i in obj) {
            if (!obj.hasOwnProperty(i)) continue;
            if (typeof obj[i] == 'object') {
                objects = objects.concat(getObjects(obj[i], key, val));    
            } else 
            //if key matches and value matches or if key matches and value is not passed (eliminating the case where key matches but passed value does not)
            if (i == key && obj[i] == val || i == key && val == '') { //
                objects.push(obj);
            } else if (obj[i] == val && key == ''){
                //only add if the object is not already in the array
                if (objects.lastIndexOf(obj) == -1){
                    objects.push(obj);
                }
            }
        }
        return objects;
    },
    startCombat = (selected) => {
        paused = false;
        resetMarker();
        Campaign().set('initiativepage', Campaign().get('playerpageid'));
        if(selected && state[state_name].config.throw_initiative){
            rollInitiative(selected, true);
        }
    },
    inFight = () => {
        return (Campaign().get('initiativepage') !== false);
    },
    rollInitiative = (selected, sort) => {
        selected.forEach(s => {
            if(s._type !== 'graphic') return;
            let token = getObj('graphic', s._id),
                //whisper = (token.get('layer') === 'gmlayer') ? '/w gm ' : '',
                bonus = parseFloat(getAttrByName(token.get('represents'), state[state_name].config.initiative_attribute_name, 'current')) || 0;
                let pr = randomBetween(1,20)+bonus;
                pr = (Math.round(pr) !== pr) ? pr.toFixed(2) : pr;
                
                addToTurnorder({ id: token.get('id'), pr, custom: '', pageid: token.get('pageid') });
        });
        if(sort){
            sortTurnorder();
        }
    },
    stopCombat = () => {
        if(timerObj) timerObj.remove();
        removeMarker();
        stopTimer();
        paused = false;
        Campaign().set({
            initiativepage: false,
            turnorder: ''
        });
        state[state_name].turnorder = {};
        round = 1;
    },
    removeMarker = () => {
        stopRotate();
        getOrCreateMarker().remove();
    },
    resetMarker = () => {
        let marker = getOrCreateMarker();
        marker.set({
            name: 'Round ' + round,
            imgsrc: state[state_name].config.marker_img,
            pageid: Campaign().get('playerpageid'),
            layer: 'gmlayer',
            left: 35, top: 35,
            width: 70, height: 70
        });
    },
    doTurnorderChange = (prev=false) => {
        if(!Campaign().get('initiativepage')) return;
        let turn = getCurrentTurn();
        if(turn.id === '-1') return;
        if(turn.id === getOrCreateMarker().get('id')){
            if(prev) PrevRound();
            else NextRound();
            return;
        }
        let token = getObj('graphic', turn.id);
		if(!token){
			return;
		}
        toFront(token);
        if(state[state_name].config.timer.use_timer){
            startTimer(token);
        }
        changeMarker(token || false);
        if(state[state_name].config.announcements.announce_turn){
            announceTurn(token || turn.custom, (token.get('layer') === 'objects') ? '' : 'gm');
        }else if(state[state_name].config.announcements.announce_conditions){
            let name = token.get('name') || turn.custom;
            let conditions = getConditionString(token);
            if(conditions && conditions !== '') makeAndSendMenu(conditions, 'Conditions - ' + name, (token.get('layer') === 'objects') ? '' : 'gm');
        }
        Pull(token);
        doFX(token);
    },
    doFX = (token) => {
        if(!state[state_name].config.announcements.use_fx) return;
        let pos = {x: token.get('left'), y: token.get('top')};
        spawnFxBetweenPoints(pos, pos, state[state_name].config.announcements.fx_type, token.get('pageid'));
    },
    Pull = (token) => {
        if(!state[state_name].config.pull) return;
        sendPing(token.get('left'), token.get('top'), token.get('pageid'), null, true);
    },
    startTimer = (token) => {
        paused = false;
        clearInterval(intervalHandle);
        if(timerObj) timerObj.remove();
        let config_time = parseInt(state[state_name].config.timer.time); 
        let time = config_time;
        if(token && state[state_name].config.timer.token_timer){
            timerObj = createObj('text', {
                text: 'Timer: ' + time,
                font_size: state[state_name].config.timer.token_font_size,
                font_family: state[state_name].config.timer.token_font,
                color: state[state_name].config.timer.token_font_color,
                pageid: token.get('pageid'),
                layer: 'gmlayer'
            });
        }
        intervalHandle = setInterval(() => {
            if(paused) return;
            if(timerObj) timerObj.set({
                top: token.get('top')+token.get('width')/2+40,
                left: token.get('left'),
                text: 'Timer: ' + time,
                layer: token.get('layer')
            });
            if(state[state_name].config.timer.chat_timer && (time === config_time || config_time/2 === time || config_time/4 === time || time === 10 || time === 5)){
                makeAndSendMenu('', 'Time Remaining: ' + time);
            }
            if(time <= 0){
                if(timerObj) timerObj.remove();
                clearInterval(intervalHandle);
                NextTurn();
            }
            time--;
        }, 1000);
    },
    stopTimer = () => {
        clearInterval(intervalHandle);
        if(timerObj) timerObj.remove();
    },
    pauseTimer = () => {
        paused = !paused;
    },
    announceTurn = (token, target) => {
        let name, imgurl;
        if(typeof token === 'object'){
            name = token.get('name');
            imgurl = token.get('imgsrc');
        }else{
            name = token;
        }
        let conditions = getConditionString(token);
        let image = (imgurl) ? '<img src="'+imgurl+'" width="50px" height="50px"  />' : '';
        name = (state[state_name].config.announcements.handleLongName) ? handleLongString(name) : name;
        let contents = '\
        <table> \
          <tr> \
            <td>'+image+'</td> \
            <td style="padding-left: 5px;"><span style="font-size: 16pt;">'+name+'\'s Turn</span></td> \
          </tr> \
        </table> \
        <div style="overflow: hidden"> \
          <div style="float: left">'+conditions+'</div> \
          ' + makeButton('Done', '!'+state[state_name].config.command+' next', styles.button + styles.float.right) +' \
        </div>';
        makeAndSendMenu(contents, '', target);
    },
    getConditionString = (token) => {
        let name = strip(token.get('name')).toLowerCase();
        let conditionsSTR = '';
        if(state[state_name].conditions[name] && state[state_name].conditions[name].length){
            state[state_name].conditions[name].forEach((condition, i) => {
                if(typeof condition.duration === 'undefined' || condition.duration === false){
                    conditionsSTR += '<b>'+condition.name+'</b><br>';
                }else if(condition.duration <= 1){
                    conditionsSTR += '<b>'+condition.name+'</b> removed.<br>';
                    removeCondition(token, condition.name, true);
                }else{
                    state[state_name].conditions[name][i].duration = parseInt(state[state_name].conditions[name][i].duration)+parseInt(condition.direction);
                    conditionsSTR += '<b>'+condition.name+'</b>: ' + condition.duration + '<br>';
                }
                conditionsSTR += (condition.message) ? '<i style="font-size: 10pt">'+condition.message+'</i><br>' : '';
            });
        }
        return conditionsSTR;
    },
    handleLongString = (str, max=8) => {
        str = str.split(' ')[0];
        return (str.length > max) ? str.slice(0, max) + '...' : str;
    },
    NextTurn = () => {
        let turnorder = getTurnorder(),
            current_turn = turnorder.shift();
        turnorder.push(current_turn);
        setTurnorder(turnorder);
        doTurnorderChange();
    },
    PrevTurn = () => {
        let turnorder = getTurnorder(),
            last_turn = turnorder.pop();        
        turnorder.unshift(last_turn);
        setTurnorder(turnorder);
        doTurnorderChange(true);
    },
    NextRound = () => {
        let marker = getOrCreateMarker();
        round++;
        marker.set({ name: 'Round ' + round});
        if(state[state_name].config.announcements.announce_round){
            let text = '<span style="font-size: 16pt; font-weight: bold;">'+marker.get('name')+'</span>';
            makeAndSendMenu(text);
        }
        NextTurn();
    },
    PrevRound = () => {
        let marker = getOrCreateMarker();
        round--;
        marker.set({ name: 'Round ' + round});
        if(state[state_name].config.announcements.announce_round){
            let text = '<span style="font-size: 16pt; font-weight: bold;">'+marker.get('name')+'</span>';
            makeAndSendMenu(text);
        }
        PrevTurn();
    },
    changeMarker = (token) => {
        let marker = getOrCreateMarker();
        if(!token){
            resetMarker();
            return;
        }
        let settings = {
            layer: token.get('layer'),
            top: token.get('top'),
            left: token.get('left'),
            width: token.get('width')+(token.get('width')*0.35),
            height: token.get('height')+(token.get('height')*0.35)
        };
        marker.set(settings);
        toBack(marker);
    },
    getOrCreateMarker = () => {
        let marker,
            img = state[state_name].config.marker_img,
            playerpageid = Campaign().get('playerpageid'),
            markers = findObjs({
                pageid: playerpageid,
                imgsrc: img
            });
        markers.forEach((marker, i) => {
            if(i > 0) marker.remove();
        });
        marker = markers.shift();
        if(!marker) {
            marker = createObj('graphic', {
                name: 'Round 0',
                imgsrc: img,
                pageid: playerpageid,
                layer: 'gmlayer',
                showplayers_name: true,
                left: 35, top: 35,
                width: 70, height: 70
            });
        }
        checkMarkerturn(marker);
        toBack(marker);
        //startRotate(marker);
        return marker;
    },
/*
    startRotate = (token) => {
        clearInterval(rotationInterval);
        let i = 0;
        rotationInterval = setInterval(() => {
            i += 2;
            log(i);
            if(i >= 360) i = 0;
            token.set('rotation', i);
        }, 50);
    },
*/
    stopRotate = () => {
        clearInterval(rotationInterval);
    },
    checkMarkerturn = (marker) => {
        let turnorder = getTurnorder(),
            hasTurn = false;
        turnorder.forEach(turn => {
            if(turn.id === marker.get('id')) hasTurn = true;
        });
        if(!hasTurn){
            turnorder.push({ id: marker.get('id'), pr: -1, custom: '', pageid: marker.get('pageid') });
            Campaign().set('turnorder', JSON.stringify(turnorder));
        }
    },
    sortTurnorder = (order='DESC') => {
        let turnorder = getTurnorder();
        turnorder.sort((a,b) => { 
            return (order === 'ASC') ? a.pr - b.pr : b.pr - a.pr;
        });
        setTurnorder(turnorder);
        doTurnorderChange();
    },
    getTurnorder = () => {
        return (Campaign().get('turnorder') === '') ? [] : Array.from(JSON.parse(Campaign().get('turnorder')));
    },
    getCurrentTurn = () => {
        return getTurnorder().shift();
    },
    addToTurnorder = (turn) => {
		if(!turn){
			return;
		}
        let turnorder = getTurnorder(),
            justDoIt = true;
        turnorder.forEach(t => {
            if(t.id === turn.id) justDoIt = false;
        });
        if(justDoIt){
            turnorder.push(turn);
            setTurnorder(turnorder);
        }
    },
    setTurnorder = (turnorder) => {
        Campaign().set('turnorder', JSON.stringify(turnorder));
    },
    randomBetween = (min, max) => {
        return Math.floor(Math.random()*(max-min+1)+min);
    },
    sendTokenConditionMenu = (tokens) => {
        let contents = '<table style="width: 100%;">';
        let i = 0;
        tokens.forEach(token => {
            if(!token) return;
            let conditions = state[state_name].conditions[strip(token.get('name')).toLowerCase()];
            if(i) contents += '<tr><td colspan="2"><hr></td></tr>';
            i++;
            contents += ' \
                <tr> \
                    <td colspan="2" style="font-size: 12pt; font-weight: bold;"> \
                        <img src='+token.get('imgsrc')+' style="width: 32px; height: 32px; vertical-align: middle;" /> \
                        <span style="vertical-align: middle;">'+token.get('name')+'</span> \
                    </td> \
                </tr>';
            if(!conditions || !conditions.length){
                contents += '<tr><td colspan="2" style="text-align: center;"><i>None</i></td></tr>';
            }else{
                conditions.forEach(condition => {
                    let removeButton = makeButton('<img src="<a href="https://s3.amazonaws.com/files.d20.io/images/11381509/YcG-o2Q1-CrwKD_nXh5yAA/thumb.png?1439051579" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/11381509/YcG-o2Q1-CrwKD_nXh5yAA/thumb.png?1439051579</a>" />', '!'+state[state_name].config.command + ' remove ' + condition.name + ' ' + token.get('id'), styles.button + styles.float.right + 'width: 16px; height: 16px;');
                    contents += ' \
                    <tr> \
                        <td style="text-align: center">'+condition.name+'</td> \
                        <td>'+removeButton+'</td> \
                    </tr>';
                });
            }
        });
        contents += '</table>';
        makeAndSendMenu(contents, '', 'gm');
    },
    sendConditionsMenu = () => {
        let addButton;
        let SI_listItems = [];
        if(extensions.StatusInfo){
            Object.keys(StatusInfo.getConditions()).map(key => StatusInfo.getConditions()[key]).forEach(condition => {
                let conditionSTR = condition.name + ' ?{Duration} ?{Direction|-1} ?{Message}';
                addButton = makeButton(StatusInfo.getIcon(condition.icon, 'margin-right: 5px; margin-top: 5px; display: inline-block;') + condition.name, '!'+state[state_name].config.command + ' add ' + conditionSTR, styles.textButton);
                SI_listItems.push('<span style="'+styles.float.left+'">'+addButton+'</span>');
            });
        }
        let F_listItems = [];
        Object.keys(state[state_name].favorites).map(key => state[state_name].favorites[key]).forEach(condition => {
            let conditionSTR = (!condition.duration) ? condition.name : condition.name + ' ' + condition.duration + ' ' + condition.direction + ' ' + condition.message;
            addButton = makeButton(condition.name, '!'+state[state_name].config.command + ' add ' + conditionSTR, styles.textButton);
            F_listItems.push('<span style="'+styles.float.left+'">'+addButton+' - <span style="font-size: 8pt">'+condition.duration+':'+condition.direction+':'+condition.message+'</span></span>');
        });
        let contents = '';
        contents += '<h4>StatusInfo Conditions</h4>';
        if(SI_listItems.length){
            contents += makeList(SI_listItems, styles.reset + styles.list + styles.overflow, styles.overflow);
        }else{
            contents += (extensions.StatusInfo) ? 'Your StatusInfo doesn\'t have any conditions.' : makeButton('StatusInfo', '<a href="https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo" rel="nofollow">https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo</a>', styles.textButton) + ' is not installed.';
        }
        contents += '<hr>';
        contents += '<h4>Favorite Conditions</h4>';
        if(F_listItems.length){
            contents += makeList(F_listItems, styles.reset + styles.list + styles.overflow, styles.overflow);
        }else{
            contents += 'You don\'t have any favorite conditions yet.';
        }
        contents += '<br><br>' + makeButton('Edit Favorites', '!'+state[state_name].config.command + ' favorites', styles.button + styles.fullWidth);
        makeAndSendMenu(contents, 'Conditions', 'gm');
    },
    sendFavoritesMenu = () => {
        let addButton, editButton, list;
        let listItems = [];
        Object.keys(state[state_name].favorites).map(key => state[state_name].favorites[key]).forEach(condition => {
            let conditionSTR = (!condition.duration) ? condition.name : condition.name + ' ' + condition.duration + ' ' + condition.direction + ' ' + condition.message;
            addButton = makeButton(condition.name, '!'+state[state_name].config.command + ' add ' + conditionSTR, styles.textButton);
            editButton = makeButton('Edit', '!'+state[state_name].config.command + ' editfav ' + condition.name, styles.button + styles.float.right);
            listItems.push('<span style="'+styles.float.left+'">'+addButton+'</span> '+editButton);
        });
        let newButton = makeButton('Add New', '!'+state[state_name].config.command + ' addfav ?{Name} ?{Duration} ?{Direction} ?{Message}', styles.button + styles.fullWidth);
        list = (listItems.length) ? makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow) : 'No favorites yet.';
        makeAndSendMenu(list + '<hr>' + newButton, 'Favorite Conditions', 'gm');
    },
    sendEditFavoriteConditionMenu = (condition) => {
        if(!state[state_name].favorites[strip(condition.name).toLowerCase()]){
            makeAndSendMenu('Condition does not exist.', '', 'gm');
            return;
        }
        let nameButton = makeButton(condition.name, '!'+state[state_name].config.command + ' editfav ' + condition.name + ' name|?{Name|'+condition.name+'}', styles.button + styles.float.right);
        let durationButton = makeButton(condition.duration, '!'+state[state_name].config.command + ' editfav ' + condition.name + ' duration|?{Duration|'+condition.duration+'}', styles.button + styles.float.right);
        let directionButton = makeButton(condition.direction, '!'+state[state_name].config.command + ' editfav ' + condition.name + ' direction|?{Direction|'+condition.direction+'}', styles.button + styles.float.right);
        let listItems = [
            '<span style="'+styles.float.left+'">Name</span> '+nameButton,
            '<span style="'+styles.float.left+'">Duration</span> '+durationButton
        ];
        if(condition.duration && condition.duration !== 0 && condition.duration !== '0'){
            listItems.push('<span style="'+styles.float.left+'">Direction</span> '+directionButton);
        }
        let removeButton = makeButton('Remove', '!'+state[state_name].config.command + ' removefav ' + condition.name, styles.button + styles.fullWidth);
        let backButton = makeButton('Back', '!'+state[state_name].config.command + ' favorites', styles.button + styles.fullWidth);
        let messageButton = makeButton((condition.message) ? 'Change Message' : 'Set Message', '!'+state[state_name].config.command + ' editfav ' + condition.name + ' message|?{Message|'+condition.message+'}', styles.button);
        let message = (condition.message) ? condition.message : '<i>None</i>';
        makeAndSendMenu(makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow) + '<hr><b>Message</b><p>' + message + '</p>' + messageButton + '<hr>' + removeButton + '<hr>' + backButton, 'Edit - ' + condition.name, 'gm');
    },
    sendConfigMenu = (first, message) => {
        let commandButton = makeButton('!'+state[state_name].config.command, '!' + state[state_name].config.command + ' config command|?{Command (without !)}', styles.button + styles.float.right),
            markerImgButton = makeButton('<img src="'+state[state_name].config.marker_img+'" width="30px" height="30px" />', '!' + state[state_name].config.command + ' config marker_img|?{Image Url}', styles.button + styles.float.right),
            throwIniButton = makeButton(state[state_name].config.throw_initiative, '!' + state[state_name].config.command + ' config throw_initiative|'+!state[state_name].config.throw_initiative, styles.button + styles.float.right),
            iniAttrButton = makeButton(state[state_name].config.initiative_attribute_name, '!' + state[state_name].config.command + ' config initiative_attribute_name|?{Attribute|'+state[state_name].config.initiative_attribute_name+'}', styles.button + styles.float.right),
            closeStopButton = makeButton(state[state_name].config.close_stop, '!' + state[state_name].config.command + ' config close_stop|'+!state[state_name].config.close_stop, styles.button + styles.float.right),
            pullButton = makeButton(state[state_name].config.pull, '!' + state[state_name].config.command + ' config pull|'+!state[state_name].config.pull, styles.button + styles.float.right),
            listItems = [
                '<span style="'+styles.float.left+'">Command:</span> ' + commandButton,
                '<span style="'+styles.float.left+'">Ini. Attribute:</span> ' + iniAttrButton,
                '<span style="'+styles.float.left+'">Marker Img:</span> ' + markerImgButton,
                '<span style="'+styles.float.left+'">Stop on close:</span> ' + closeStopButton,
                '<span style="'+styles.float.left+'">Auto Roll Ini.:</span> ' + throwIniButton,
                '<span style="'+styles.float.left+'">Auto Pull Map:</span> ' + pullButton
            ],
            configTimerButton = makeButton('Timer Config', '!'+state[state_name].config.command + ' config timer', styles.button),
            configAnnouncementsButton = makeButton('Announcement Config', '!'+state[state_name].config.command + ' config announcements', styles.button),
            resetButton = makeButton('Reset', '!' + state[state_name].config.command + ' reset', styles.button + styles.fullWidth),
            title_text = (first) ? script_name + ' First Time Setup' : script_name + ' Config';
        message = (message) ? '<p>'+message+'</p>' : '';
        let contents = message+makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow)+configTimerButton+configAnnouncementsButton+'<hr><p style="font-size: 80%">You can always come back to this config by typing `!'+state[state_name].config.command+' config`.</p><hr>'+resetButton;
        makeAndSendMenu(contents, title_text, 'gm');
    },
    sendConfigAnnounceMenu = () =>{
        let announceTurnButton = makeButton(state[state_name].config.announcements.announce_turn, '!' + state[state_name].config.command + ' config announcements announce_turn|'+!state[state_name].config.announcements.announce_turn, styles.button + styles.float.right),
            announceRoundButton = makeButton(state[state_name].config.announcements.announce_round, '!' + state[state_name].config.command + ' config announcements announce_round|'+!state[state_name].config.announcements.announce_round, styles.button + styles.float.right),
            announceConditionsButton = makeButton(state[state_name].config.announcements.announce_conditions, '!' + state[state_name].config.command + ' config announcements announce_conditions|'+!state[state_name].config.announcements.announce_conditions, styles.button + styles.float.right),
            handleLongNameButton = makeButton(state[state_name].config.announcements.handleLongName, '!' + state[state_name].config.command + ' config announcements handleLongName|'+!state[state_name].config.announcements.handleLongName, styles.button + styles.float.right),
            useFXButton = makeButton(state[state_name].config.announcements.use_fx, '!' + state[state_name].config.command + ' config announcements use_fx|'+!state[state_name].config.announcements.use_fx, styles.button + styles.float.right),
            FXTypeButton = makeButton(state[state_name].config.announcements.fx_type, '!' + state[state_name].config.command + ' config announcements fx_type|?{Type|'+state[state_name].config.announcements.fx_type+'}', styles.button + styles.float.right),
            backButton = makeButton('< Back', '!'+state[state_name].config.command + ' config', styles.button + styles.fullWidth),
            listItems = [];
        listItems.push('<span style="'+styles.float.left+'">Announce Round:</span> ' + announceRoundButton);
        listItems.push('<span style="'+styles.float.left+'">Announce Turn:</span> ' + announceTurnButton);
    
        if(!state[state_name].config.announcements.announce_turn){
            listItems.push('<span style="'+styles.float.left+'">Announce Conditions:</span> ' + announceConditionsButton);
        }
        if(state[state_name].config.announcements.announce_turn){
            listItems.push('<span style="'+styles.float.left+'">Shorten Long Name:</span> ' + handleLongNameButton);
        }
        listItems.push('<span style="'+styles.float.left+'">Use FX:</span> ' + useFXButton);
        if(state[state_name].config.announcements.use_fx){
            listItems.push('<span style="'+styles.float.left+'">FX Type:</span> ' + FXTypeButton);
        }
        let contents = makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow)+'<hr>'+backButton;
        makeAndSendMenu(contents, script_name + ' Announcements Config', 'gm');
    },
    sendConfigTimerMenu = () => {
        let turnTimerButton = makeButton(state[state_name].config.timer.use_timer, '!' + state[state_name].config.command + ' config timer use_timer|'+!state[state_name].config.timer.use_timer, styles.button + styles.float.right),
            timeButton = makeButton(state[state_name].config.timer.time, '!' + state[state_name].config.command + ' config timer time|?{Time|'+state[state_name].config.timer.time+'}', styles.button + styles.float.right),
            chatTimerButton = makeButton(state[state_name].config.timer.chat_timer, '!' + state[state_name].config.command + ' config timer chat_timer|'+!state[state_name].config.timer.chat_timer, styles.button + styles.float.right),
            tokenTimerButton = makeButton(state[state_name].config.timer.token_timer, '!' + state[state_name].config.command + ' config timer token_timer|'+!state[state_name].config.timer.token_timer, styles.button + styles.float.right),
            tokenFontButton = makeButton(state[state_name].config.timer.token_font, '!' + state[state_name].config.command + ' config timer token_font|?{Font|Arial|Patrick Hand|Contrail|Light|Candal}', styles.button + styles.float.right),
            tokenFontSizeButton = makeButton(state[state_name].config.timer.token_font_size, '!' + state[state_name].config.command + ' config timer token_font_size|?{Font Size|'+state[state_name].config.timer.token_font_size+'}', styles.button + styles.float.right),
            backButton = makeButton('< Back', '!'+state[state_name].config.command + ' config', styles.button + styles.fullWidth),
            listItems = [
                '<span style="'+styles.float.left+'">Turn Timer:</span> ' + turnTimerButton,
                '<span style="'+styles.float.left+'">Time:</span> ' + timeButton,
                '<span style="'+styles.float.left+'">Show in Chat:</span> ' + chatTimerButton,
                '<span style="'+styles.float.left+'">Show on Token:</span> ' + tokenTimerButton,
                '<span style="'+styles.float.left+'">Token Font:</span> ' + tokenFontButton,
                '<span style="'+styles.float.left+'">Token Font Size:</span> ' + tokenFontSizeButton
            ];
        let contents = makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow)+'<hr>'+backButton;
        makeAndSendMenu(contents, script_name + ' Timer Config', 'gm');
    },
    sendMenu = () => {
        let nextButton = makeButton('Next Turn', '!' + state[state_name].config.command + ' next b', styles.button),
            prevButton = makeButton('Prev. Turn', '!' + state[state_name].config.command + ' prev b', styles.button),
            startCombatButton = makeButton('Start Combat', '!' + state[state_name].config.command + ' start b', styles.button),
            stopCombatButton = makeButton('Stop Combat', '!' + state[state_name].config.command + ' stop b', styles.button),
            pauseTimerTitle = (paused) ? 'Start Timer' : 'Pause Timer',
            pauseTimerButton = makeButton(pauseTimerTitle, '!' + state[state_name].config.command + ' pt b', styles.button),
            stopTimerButton = makeButton('Stop Timer', '!' + state[state_name].config.command + ' st b', styles.button),
            addConditionButton = makeButton('Add Condition', '!' + state[state_name].config.command + ' add ?{Condition} ?{Duration}', styles.button),
            removeConditionButton = makeButton('Remove Condition', '!' + state[state_name].config.command + ' remove ?{Condition}', styles.button),
            resetConditionsButton = makeButton('Reset Conditions', '!'+state[state_name].config.command + ' reset conditions', styles.button),
            favoritesButton = makeButton('Favorite Conditions', '!'+state[state_name].config.command + ' favorites', styles.button),
            contents;
        
        if(inFight()){
            contents = ' \
            '+nextButton+prevButton+'<br> \
            '+pauseTimerButton+stopTimerButton+' \
            <hr> \
            <b>With Selected:</b><br> \
            '+addConditionButton+'<br> \
            '+removeConditionButton+' \
            <hr> \
            '+favoritesButton+' \
            <hr> \
            '+stopCombatButton+'<br> \
            '+resetConditionsButton;
        }else{
            contents = ' \
            '+startCombatButton+' \
            <hr> \
            '+favoritesButton;
        }
        makeAndSendMenu(contents, script_name + ' Menu', 'gm');
    },
    sendHelpMenu = () => {
        let configButton = makeButton('Config', '!' + state[state_name].config.command + ' config', styles.button + styles.fullWidth);
        let listItems = [
            '<span style="'+styles.underline+'">!'+state[state_name].config.command+' help</span> - Shows this menu.',
            '<span style="'+styles.underline+'">!'+state[state_name].config.command+' config</span> - Shows the configuration menu.'
        ];
        let contents = '<b>Commands:</b>'+makeList(listItems, styles.reset + styles.list)+'<hr>'+configButton;
        makeAndSendMenu(contents, script_name + ' Help', 'gm');
    },
    makeAndSendMenu = (contents, title, whisper) => {
        title = (title && title != '') ? makeTitle(title) : '';
        whisper = (whisper && whisper !== '') ? '/w ' + whisper + ' ' : '';
        sendChat(script_name, whisper + '<div style="'+styles.menu+styles.overflow+'">'+title+contents+'</div>', null, {noarchive:true});
    },
    makeTitle = (title) => {
        return '<h3 style="margin-bottom: 10px;">'+title+'</h3>';
    },
    makeButton = (title, href, style) => {
        return '<a style="'+style+'" href="'+href+'">'+title+'</a>';
    },
    makeList = (items, listStyle, itemStyle) => {
        let list = '<ul style="'+listStyle+'">';
        items.forEach((item) => {
            list += '<li style="'+itemStyle+'">'+item+'</li>';
        });
        list += '</ul>';
        return list;
    },
    checkStatusInfo = () => {
        if(typeof StatusInfo === 'undefined'){
            makeAndSendMenu('Consider installing '+makeButton('StatusInfo', '<a href="https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo" rel="nofollow">https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo</a>', styles.textButton)+' it works great with this script.', '', 'gm');
            return;
        }
        if(!StatusInfo.version || StatusInfo.version !== "0.3.8"){
            makeAndSendMenu('Please update '+makeButton('StatusInfo', '<a href="https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo" rel="nofollow">https://github.com/RobinKuiper/Roll20APIScripts/tree/master/StatusInfo</a>', styles.textButton)+' to the latest version.', '', 'gm');
            return;
        }
        extensions.StatusInfo = true;
    },
    checkInstall = () => {
        if(!_.has(state, state_name)){
            state[state_name] = state[state_name] || {};
        }
        setDefaults();
        checkStatusInfo();
        log(script_name + ' Ready! Command: !'+state[state_name].config.command);
        if(state[state_name].config.debug){
			makeAndSendMenu(script_name + ' Ready! Debug On.', '', 'gm');
		}
    },
    handeIniativePageChange = (obj,prev) => {
        if(state[state_name].config.close_stop && (obj.get('initiativepage') !== prev.initiativepage && !obj.get('initiativepage'))){
            stopCombat();
        }
    },
    observeTokenChange = function(handler){
        if(handler && _.isFunction(handler)){
            observers.tokenChange.push(handler);
        }
    },
    notifyObservers = function(event,obj,prev){
        _.each(observers[event],function(handler){
            handler(obj,prev);
        });
    },
    registerEventHandlers = () => {
        on('chat:message', handleInput);
        on('change:campaign:turnorder', handleTurnorderChange);
        on('change:campaign:initiativepage', handeIniativePageChange);
        on('change:graphic:top', handleGraphicMovement);
        on('change:graphic:left', handleGraphicMovement);
        on('change:graphic:layer', handleGraphicMovement);
        on('change:graphic:statusmarkers', handleStatusMarkerChange);
        if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
            TokenMod.ObserveTokenChange(function(obj,prev){
                handleStatusMarkerChange(obj,prev);
            });
        }
    },
    setDefaults = (reset) => {
        const defaults = {
            config: {
                command: 'ct',
                marker_img: '<a href="https://s3.amazonaws.com/files.d20.io/images/52550079/U-3U950B3wk_KRtspSPyuw/thumb.png?1524507826" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/52550079/U-3U950B3wk_KRtspSPyuw/thumb.png?1524507826</a>',
                throw_initiative: true,
                initiative_attribute_name: 'initiative_bonus',
                close_stop: true,
                pull: true,
                timer: {
                    use_timer: true,
                    time: 120,
                    chat_timer: true,
                    token_timer: true,
                    token_font: 'Candal',
                    token_font_size: 16,
                    token_font_color: 'rgb(255, 0, 0)'
                },
                announcements: {
                    announce_conditions: false,
                    announce_turn: true,
                    announce_round: true,
                    handleLongName: true,
                    use_fx: false,
                    fx_type: 'nova-holy'
                }
            },
            conditions: {},
            favorites: {}
        };
        if(!state[state_name].config){
            state[state_name].config = defaults.config;
        }else{
            if(!state[state_name].config.hasOwnProperty('command')){
                state[state_name].config.command = defaults.config.command;
            }
            if(!state[state_name].config.hasOwnProperty('marker_img')){
                state[state_name].config.marker_img = defaults.config.marker_img;
            }
            if(!state[state_name].config.hasOwnProperty('throw_initiative')){
                state[state_name].config.throw_initiative = defaults.config.throw_initiative;
            }
            if(!state[state_name].config.hasOwnProperty('initiative_attribute_name')){
                state[state_name].config.initiative_attribute_name = defaults.config.initiative_attribute_name;
            }
            if(!state[state_name].config.hasOwnProperty('close_stop')){
                state[state_name].config.close_stop = defaults.config.close_stop;
            }
            if(!state[state_name].config.hasOwnProperty('pull')){
                state[state_name].config.pull = defaults.config.pull;
            }
            if(!state[state_name].config.hasOwnProperty('timer')){
                state[state_name].config.timer = defaults.config.timer;
            }else{
                if(!state[state_name].config.timer.hasOwnProperty('use_timer')){
                    state[state_name].config.timer.use_timer = defaults.config.timer.use_timer;
                }
                if(!state[state_name].config.timer.hasOwnProperty('time')){
                    state[state_name].config.timer.time = defaults.config.timer.time;
                }
                if(!state[state_name].config.timer.hasOwnProperty('chat_timer')){
                    state[state_name].config.timer.chat_timer = defaults.config.timer.chat_timer;
                }
                if(!state[state_name].config.timer.hasOwnProperty('token_timer')){
                    state[state_name].config.timer.token_timer = defaults.config.timer.token_timer;
                }
                if(!state[state_name].config.timer.hasOwnProperty('token_font')){
                    state[state_name].config.timer.token_font = defaults.config.timer.token_font;
                }
                if(!state[state_name].config.timer.hasOwnProperty('token_font_size')){
                    state[state_name].config.timer.token_font_size = defaults.config.timer.token_font_size;
                }
                if(!state[state_name].config.timer.hasOwnProperty('token_font_color')){
                    state[state_name].config.timer.token_font_color = defaults.config.timer.token_font_color;
                }
            }
            if(!state[state_name].config.hasOwnProperty('announcements')){
                state[state_name].config.announcements = defaults.config.announcements;
            }else{
                if(!state[state_name].config.announcements.hasOwnProperty('announce_turn')){
                    state[state_name].config.announcements.announce_turn = defaults.config.announcements.announce_turn;
                }
                if(!state[state_name].config.announcements.hasOwnProperty('announce_round')){
                    state[state_name].config.announcements.announce_round = defaults.config.announcements.announce_round;
                }
                if(!state[state_name].config.announcements.hasOwnProperty('announce_conditions')){
                    state[state_name].config.announcements.announce_conditions = defaults.config.announcements.announce_conditions;
                }
                if(!state[state_name].config.announcements.hasOwnProperty('handleLongName')){
                    state[state_name].config.announcements.handleLongName = defaults.config.announcements.handleLongName;
                }
                if(!state[state_name].config.announcements.hasOwnProperty('use_fx')){
                    state[state_name].config.announcements.use_fx = defaults.config.announcements.use_fx;
                }
                if(!state[state_name].config.announcements.hasOwnProperty('fx_type')){
                    state[state_name].config.announcements.fx_type = defaults.config.announcements.fx_type;
                }
            }
        }
        if(!state[state_name].hasOwnProperty('conditions')){
            state[state_name].conditions = defaults.conditions;
        }
        if(!state[state_name].hasOwnProperty('favorites')){
            state[state_name].favorites = defaults.favorites;
        }
        if(!state[state_name].config.hasOwnProperty('firsttime') && !reset){
            sendConfigMenu(true);
            state[state_name].config.firsttime = false;
        }
    };
    return {
        CheckInstall: checkInstall,
        RegisterEventHandlers: registerEventHandlers,
        ObserveTokenChange: observeTokenChange
    };
})();
on('ready',function() {
    'use strict';
    CombatTracker.CheckInstall();
    CombatTracker.RegisterEventHandlers();
});
/*
conditions = {
    xandir: [
        { name: 'prone', duration: '1' }
    ]
}
*/