AWBW Colourblind support

Swap player colours to Orange Star and Blue Moon, or click a player's nation logo to assign a different colour to them.

// ==UserScript==
// @name         AWBW Colourblind support
// @namespace    https://awbw.amarriner.com/
// @version      1.06
// @description  Swap player colours to Orange Star and Blue Moon, or click a player's nation logo to assign a different colour to them.
// @author       Truniht
// @match        https://awbw.amarriner.com/*?games_id=*
// @match        https://awbw.amarriner.com/*?replays_id=*
// @icon         https://awbw.amarriner.com/favicon.ico
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    //Nation to assign to players
    var nationAssign = [
        "os",
        "bm",
        "ge",
        "bh",
        "pl",
        "tg"
    ];

    var nationAssignSwapped = [
        "bm",
        "os",
        "bh",
        "ge",
        "tg",
        "pl"
    ];

    //Nation information
    var nations = {
        "os": {id: "os", name:'Orange Star', building: '/orangestar', moved: '/gs_os', unit: '/os', playerColour: 'linear-gradient(to right, rgb(255, 79, 78), rgb(146, 50, 67))'},
        "bm": {id: "bm", name:'Blue Moon', building: '/bluemoon', moved: '/gs_bm', unit: '/bm', playerColour: 'linear-gradient(to right, rgb(148, 162, 253), rgb(70,110,254))'},
        "ge": {id: "ge", name:'Green Earth', building: '/greenearth', moved: '/gs_ge', unit: '/ge', playerColour: 'linear-gradient(to right, rgb(135, 226, 135), rgb(61, 194, 45))'},
        "yc": {id: "yc", name:'Yellow Comet', building: '/yellowcomet', moved: '/gs_yc', unit: '/yc', playerColour: 'linear-gradient(to right, rgb(240, 210, 4), rgb(201, 189, 2))'},
        "bh": {id: "bh", name:'Black Hole', building: '/blackhole', moved: '/gs_bh', unit: '/bh', playerColour: 'linear-gradient(to right, rgb(187, 180, 165), rgb(116, 89, 138))'},
        "rf": {id: "rf", name:'Red Fire', building: '/redfire', moved: '/gs_rf', unit: '/rf', playerColour: 'linear-gradient(to right, rgb(194, 113, 132), rgb(181, 39, 68))'},
        "gs": {id: "gs", name:'Grey Sky', building: '/greysky', moved: '/gs_gs', unit: '/gs', playerColour: 'linear-gradient(to right, rgb(151, 151, 151), rgb(114, 114, 114))'},
        "bd": {id: "bd", name:'Brown Desert', building: '/browndesert', moved: '/gs_bd', unit: '/bd', playerColour: 'linear-gradient(to right, rgb(173, 126, 95), rgb(152, 83, 51))'},
        "ab": {id: "ab", name:'Amber Blaze', building: '/amberblaze', moved: '/gs_ab', unit: '/ab', playerColour: 'linear-gradient(to right, rgb(254, 192, 120), rgb(252, 163, 57))'},
        "js": {id: "js", name:'Jade Sun', building: '/jadesun', moved: '/gs_js', unit: '/js', playerColour: 'linear-gradient(to right, rgb(196, 215, 180), rgb(166, 182, 153))'},
        "ci": {id: "ci", name:'Cobalt Ice', building: '/cobaltice', moved: '/gs_ci', unit: '/ci', playerColour: 'linear-gradient(to right, rgb(35, 66, 186), rgb(11, 32, 112))'},
        "pc": {id: "pc", name:'Pink Cosmos', building: '/pinkcosmos', moved: '/gs_pc', unit: '/pc', playerColour: 'linear-gradient(to right, rgb(255, 153, 204), rgb(255, 102, 204))'},
        "tg": {id: "tg", name:'Teal Galaxy', building: '/tealgalaxy', moved: '/gs_tg', unit: '/tg', playerColour: 'linear-gradient(to right, rgb(108, 217, 208), rgb(60, 205, 193))'},
        "pl": {id: "pl", name:'Purple Lightning', building: '/purplelightning', moved: '/gs_pl', unit: '/pl', playerColour: 'linear-gradient(to right, rgb(164, 71, 211), rgb(111, 26, 155))'},
        "ar": {id: "ar", name:'Acid Rain', building: '/acidrain', moved: '/gs_ar', unit: '/ar', playerColour: 'linear-gradient(to right, rgb(122, 157, 17), rgb(97, 124, 14))'},
        "wn": {id: "wn", name:'White Nova', building: '/whitenova', moved: '/gs_wn', unit: '/wn', playerColour: 'linear-gradient(to right, rgb(212, 191, 159), rgb(205, 155, 154))'}
    };

    var buildingReplaceMatch = ['building'];
    var unitReplaceMatch = ['moved', 'unit'];

    //Fetch settings
    var gameID = window.location.href.match(/_id=([0-9]+)/)[1];
    var settings = GM_getValue('AWBWcbSettings' + gameID);
    if (!settings) {
        //Load legacy settings
        settings = {
            enabled: GM_getValue('AWBWenableColourBlind' + gameID) || 0,
            swap: GM_getValue('AWBWreverseColours' + gameID) || 0
        };
    }

    var documentImages = document.getElementsByTagName('img');

    //Have to use a function to get the local variable as it can't be accessed directly
    unsafeWindow.AWBWColourBlindReturnPlayers = function () {return playersInfo;}

    var loadedImages = {};
    var loadingImages = {};

    function loadImage(img, imageSource) {
        //Preload the new image and set it on load
        var newImage = loadingImages[imageSource];
        var setSource = !newImage;
        if (!newImage) newImage = loadingImages[imageSource] = new Image();

        newImage.addEventListener('load', function() {
            loadedImages[imageSource] = true;
            if (img.matchedAndChanged === imageSource) applyStyles(img);
        });

        if (setSource) newImage.src = imageSource;
    }

    function applyStyles(img) {
        img.parentNode.style.background = 'top center no-repeat url('+img.matchedAndChanged+')';
        img.parentNode.style.transform = img.style.transform;
        img.style.opacity = '0';
        img.appliedStyle = img.parentNode.style.background;
    }

    function saveSettings() {
        GM_setValue('AWBWcbSettings' + gameID, settings);
    }

    function removeColourSelect() {
        var oldEle = document.getElementById('AWBWColourBlindselectPlayerColour');
        if (oldEle) oldEle.parentNode.removeChild(oldEle);
    }

    function swapPlayerColour(playerID, playerNationID, mouseOver) {
        if (!mouseOver) removeColourSelect();

        if (!settings.players) settings.players = {};
        if (!playerNationID) delete settings.players[playerID];
        else settings.players[playerID] = playerNationID;

        var emptyPlayers = true;
        for(var i in settings.players) {
            emptyPlayers = false;
            break;
        }
        if (emptyPlayers) {
            delete settings.players;
        }
        else {
            settings.enabled = 1;
            checkBoxDiv.children[0].checked = true;
        }

        if (!mouseOver) saveSettings();
        swapColours(true);
    }
    unsafeWindow.AWBWColourBlindswapPlayerColour = swapPlayerColour;

    function selectMouseover() {
        AWBWColourBlindswapPlayerColour(this.dataPlayerID, this.nationID, true);
    }

    function selectPlayerColour() {
        removeColourSelect();

        var mainEle = document.createElement('div');

        var playerC = settings.players || {};

        for(var nationID in nations) {
            var nation = nations[nationID];
            ele = document.createElement('a');
            ele.innerHTML = '<img style="vertical-align: text-top;" src="terrain/aw2/'+nation.id+'logo.gif"> '+nation.name;
            ele.href = "javascript: AWBWColourBlindswapPlayerColour('"+this.dataPlayerID+"', '"+nation.id+"');";
            ele.style = "display: block; font-weight: bold; color: #FFFFFF; text-shadow: 1px 1px 2px black; padding: 6px; background: " + nation.playerColour + ";" +
                (playerC[this.dataPlayerID] === nation.id ? 'border: 3px #FFFFFF solid; padding: 3px;': '');
            ele.onmouseover = selectMouseover;
            ele.dataPlayerID = this.dataPlayerID;
            ele.nationID = nation.id;
            mainEle.appendChild(ele);
        }

        var ele = document.createElement('a');
        ele.innerHTML = '<img style="vertical-align: text-top;" src="'+(this.origSrc || this.src)+'"> Default / cancel';
        ele.href = "javascript: AWBWColourBlindswapPlayerColour('"+this.dataPlayerID+"');";
        ele.style = "display: block; font-weight: bold; color: #000000; background: #BBBBBB; padding: 6px; border: 1px #BBBBBB solid;";
        ele.onmouseover = selectMouseover;
        ele.dataPlayerID = this.dataPlayerID;
        ele.nationID = null;
        mainEle.appendChild(ele);

        var elePos = this.getBoundingClientRect();
        mainEle.style = "white-space: nowrap; position: absolute; border: 1px solid #000000; padding: 0px; display: block;"+
            "left: "+ (elePos.left + window.scrollX) + "px; top: "+(elePos.top + window.scrollY)+"px;";
        mainEle.id = "AWBWColourBlindselectPlayerColour";
        document.body.appendChild(mainEle);
    }

    var swapInProgress = false;
    function swapColours(force) {
        if (swapInProgress) return;
        if (!force && !settings.enabled) return;

        swapInProgress = true;
        try {
            var players = unsafeWindow.AWBWColourBlindReturnPlayers();
            var matchList = [];
            var newTeams = settings.swap ? nationAssignSwapped : nationAssign;
            for(var playerID in players) {
                var player = players[playerID];
                var newTeam = settings.players ? nations[settings.players[playerID]] : nations[newTeams[matchList.length]];

                if (document.getElementById('player'+playerID)) {
                    //Update the header colour
                    var playerHeader = document.getElementById('player'+playerID).querySelector('header');
                    if (playerHeader) {
                        if (!playerHeader.origCol) playerHeader.origCol = playerHeader.style.background;
                        playerHeader.style.background = settings.enabled && newTeam ? newTeam.playerColour : playerHeader.origCol;
                        var teamImg = playerHeader.querySelector('.player-country-logo');
                        if (!teamImg.origSrc) teamImg.origSrc = teamImg.newSrc = teamImg.src;
                        teamImg.style.cursor = 'pointer';
                        teamImg.dataPlayerID = playerID;
                        teamImg.onclick = selectPlayerColour;
                        var newSrc = settings.enabled && newTeam ? 'terrain/aw2/'+newTeam.id+'logo.gif' : teamImg.origSrc;
                        if (teamImg.newSrc != newSrc) teamImg.src = teamImg.newSrc = newSrc;
                    }
                }

                if (newTeam) {
                    matchList.push({
                        newNation: newTeam,
                        building: '/' + player.countries_name.replaceAll(' ', '').toLowerCase(), //Cities
                        moved: '/gs_' + player.countries_code, //Moved units
                        unit: '/' + player.countries_code //Units
                    });
                }
            }

            for(var img of documentImages) {
                if (img.dataLastSource !== img.src || force) {
                    img.dataLastSource = img.src;

                    var building = img.parentNode.classList.contains('game-building') || ( img.parentNode.id && img.parentNode.id.indexOf('building') !== -1);
                    var unit = !building && (img.parentNode.classList.contains('game-unit') || ( img.parentNode.id && img.parentNode.id.indexOf('unit') !== -1));
                    if (!unit && !building) continue;

                    var matched = false;
                    if (settings.enabled) {
                        var matchTypes = building ? buildingReplaceMatch : unitReplaceMatch;
                        for(var type of matchTypes) {
                            for(var i = 0; i < matchList.length; i++) {
                                var nation = matchList[i];
                                if (img.src.indexOf(nation[type]) !== -1) {
                                    var imageSource = img.src.replaceAll(nation[type], nation.newNation[type]);
                                    img.matchedAndChanged = imageSource;
                                    matched = true;

                                    if (loadedImages[imageSource]) {
                                        //Image is already loaded, set it
                                        applyStyles(img);
                                    }
                                    else {
                                        //Image has to be loaded first
                                        loadImage(img, imageSource);
                                    }
                                    break;
                                }
                            }
                            if (matched) break;
                        }
                    }

                    if (!matched && img.matchedAndChanged) {
                        //Reverse style changes to an element no longer matching
                        img.style.opacity = '';
                        img.parentNode.style.background = '';
                        img.parentNode.style.transform = '';
                        img.matchedAndChanged = false;
                        img.appliedStyle = false;
                    }
                }

                //Applied style is different for some unknown reason, reapply
                else if (img.matchedAndChanged && img.appliedStyle && img.appliedStyle !== img.parentNode.style.background) {
                    img.parentNode.style.background = img.appliedStyle;
                    img.parentNode.style.transform = img.style.transform;
                    img.style.opacity = '0';
                }
            }
        } catch (e) {}
        swapInProgress = false;
    }

    var checkBoxDiv = document.createElement('div');
    checkBoxDiv.style = 'position: absolute; top: 10px; left: 10px; z-index: 9999;';
    checkBoxDiv.innerHTML = 'Colourblind mode: '+
        '<input type="checkbox" name="colourBlindMode" '+( settings.enabled ? 'checked' : '') + '/>'+
        ', Swap sides: '+
        '<input type="checkbox" name="reverseColours" '+( settings.swap ? 'checked' : '') + '/>';
    document.body.appendChild(checkBoxDiv);

    checkBoxDiv.children[0].oninput = function() {
        settings.enabled = checkBoxDiv.children[0].checked ? 1 : 0;
        saveSettings();
        swapColours(true);
    }

    checkBoxDiv.children[1].oninput = function() {
        settings.swap = checkBoxDiv.children[1].checked ? 1 : 0;
        saveSettings();
        swapColours(true);
    }

    const observer = new MutationObserver(function() {swapColours();});
    observer.observe(document, { childList: true, subtree: true, attributes: true });

    //Run once on load to make nation icons clickable
    window.addEventListener('load', function() {swapColours(true);});
})();