MPP Second Color

Ability to have a second color in MPP

// ==UserScript==
// @name         MPP Second Color
// @namespace    http://tampermonkey.net/
// @version      1.9
// @description  Ability to have a second color in MPP
// @author       meleestars
// @license      MIT
// @match        https://multiplayerpiano.com/*
// @match        https://multiplayerpiano.net/*
// @match        https://multiplayerpiano.org/*
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACDSURBVDhPY2TABooL/kNZBAGKAXxFOURrhAG4AXKF6SRrBgGwAXqFyWRpBgGwAXYFceQb4JcfRbZmEGCMyw+jzIC8vEDKDKjL86XMgP5cD8oMmJ/jQpkBIGJ9jj3ZhoAN2J9tTZkBIHA+y5wsQ+AGgMD9LCOSDUExAAbeZ+oRaRADAwAarSM3FaaFtwAAAABJRU5ErkJggg==
// @grant        none
// ==/UserScript==

function _sc_init() {
    const _sc_version = "1.9";

    window._sc_users = {};

    let currentSecondColor = window.localStorage.getItem("secondColor") || '#777777';
    let currentAtTop = (window.localStorage.getItem("atTop") === 'true') || false;

    function _sc_isColorInvalid(sc) {
        return typeof sc !== "string" || !CSS.supports("color", sc) || sc.length !== 7;
    }

    function _sc_sendColor(sc, atTop, global, user, req) {
        if (_sc_isColorInvalid(sc)) sc = MPP.client.getOwnParticipant().color;

        window.localStorage.setItem("secondColor", sc);

        currentSecondColor = sc;

        const top = String(atTop) === 'true';

        window.localStorage.setItem("atTop", top);

        currentAtTop = top;

        if (global)
            MPP.client.sendArray([{m: "custom", data: {sc, atTop: top, requestSc: req}, target: {mode: "subscribed"}}]);
        else
            MPP.client.sendArray([{m: "custom", data: {sc, atTop: top, requestSc: req}, target: {mode: "id", id: user}}]);
    }

    function _sc_getOwnColor() {
        let secondColor = currentSecondColor;

        if (secondColor === null) {
            window.localStorage.setItem("secondColor", MPP.client.getOwnParticipant().color);
            secondColor = window.localStorage.getItem("secondColor");
        }

        if (_sc_isColorInvalid(secondColor))
            secondColor = MPP.client.getOwnParticipant().color;

        return secondColor;
    }

    function _sc_logUsers() {
        const users = [];

        for (const user of Object.entries(window._sc_users))
            users.push("`" + user[1][0] + "`");

        MPP.chat.receive({
            m: "a",
            p: {
                _id: "Second",
                name: "Color"
            },
            a: `Users that use Second Color: ${users.join(", ")}`,
            t: Date.now()
        });
    }

    function _sc_patchReply(msg) {
        const replyId = msg.r;
        console.log(msg);

        if (replyId == null)
            return;

        const replyLink = $(`#chat ul li#msg-${msg.id} .replyLink`)[0];
        const repliedUser = $(`#chat ul li#msg-${replyId}`)[0].title;
        
        if (replyLink && window._sc_users[repliedUser]) {
            const participant = MPP.client.findParticipantById(repliedUser);
            const replied = window._sc_users[repliedUser];
            const atTop = window._sc_users[repliedUser][2];

            if (atTop)
                replyLink.style.background = `linear-gradient(${replied[1]}, ${participant.color})`;
            else
                replyLink.style.background = `linear-gradient(${participant.color}, ${replied[1]})`;
        }
    }

    MPP.client.on('hi', () => MPP.client.sendArray([{m: "+custom"}]));

    MPP.client.on('ch', () => {
        let secondColor = _sc_getOwnColor();

        const participant = MPP.client.getOwnParticipant();
        window._sc_users[participant.id] = [participant.name, secondColor, currentAtTop];

        const background = currentAtTop ? `linear-gradient(${
            secondColor
        }, ${
            participant.color
        })` : `linear-gradient(${
            participant.color
        }, ${
            secondColor
        })`;

        if (participant.nameDiv)
            participant.nameDiv.style.background = background;

        if (participant.cursorDiv)
            participant.cursorDiv.getElementsByClassName("name")[0].style.background = background;

        _sc_sendColor(secondColor, currentAtTop, true, "", true);
    });

    MPP.client.on('participant added', msg => {
        _sc_sendColor(_sc_getOwnColor(), currentAtTop, false, msg.id, true);
    });

    MPP.client.on('bye', msg => {
        delete window._sc_users[msg.p];
    });

    MPP.client.on('c', msg => {
        for (let a of msg.c)
            _sc_patchReply(a);
    });

    MPP.client.on('a', msg => {
        _sc_patchReply(msg);
    });

    MPP.client.on('custom', msg => {
        if (msg.data.sc !== undefined) {
            let secondColor = msg.data.sc;
            let participant = MPP.client.findParticipantById(msg.p);

            if (_sc_isColorInvalid(secondColor))
                secondColor = participant.color;

            const background = msg.data.atTop ? `linear-gradient(${
                secondColor
            }, ${
                participant.color
            })` : `linear-gradient(${
                participant.color
            }, ${
                secondColor
            })`;

            if (participant.nameDiv)
                participant.nameDiv.style.background = background;

            if (participant.cursorDiv)
                participant.cursorDiv.getElementsByClassName("name")[0].style.background = background;

            if (participant.tag === undefined)
                participant.nameDiv.title = "This user is using Second Color.";
            else
                switch (participant.tag.text) {
                    default:
                    case '':
                    case undefined:
                        participant.nameDiv.title = "This user is using Second Color.";
                        break;

                    case 'ROOMKEEPER':
                    case 'BOT':
                        participant.nameDiv.title = "This is an authorized bot which is using Second Color.";
                        break;

                    case 'MOD':
                        participant.nameDiv.title = "This user is an official moderator of the site who is using Second Color.";
                        break;

                    case 'ADMIN':
                        participant.nameDiv.title = "This user is an official administrator of the site who is using Second Color.";
                        break;

                    case 'OWNER':
                        participant.nameDiv.title = "This user is the owner of the site who is using Second Color.";
                        break;

                    case 'MEDIA':
                        participant.nameDiv.title = "This is a well known person on Twitch, Youtube, or another platform who is using Second Color.";
                        break;
                }

            window._sc_users[participant.id] = [participant.name, secondColor, msg.data.atTop];

            if (_sc_isColorInvalid(window.localStorage.getItem("secondColor")))
                window.localStorage.setItem("secondColor", MPP.client.getOwnParticipant().color);

            if (msg.data.requestSc && msg.p !== MPP.client.getOwnParticipant().id)
                _sc_sendColor(window.localStorage.getItem("secondColor"), currentAtTop, false, msg.p);
        }
    });

    $("#rename .submit").click(() => {
        const participant = MPP.client.getOwnParticipant();

        const name = $("#rename input[name=name]").val();
        const firstColor = $("#rename input[name=color]").val();
        const secondColor = $("#rename input[name=secondcolor]").val();
        const top = document.querySelector('#rename input[id="second-color-at-top"]').checked;

        MPP.client.sendArray([{m: "userset", set: {name, color: firstColor}}]);
        window._sc_users[participant.id][1] = secondColor;

        if (secondColor !== window.localStorage.getItem("secondColor") || top !== (window.localStorage.getItem("atTop") === 'true'))
            _sc_sendColor(secondColor, top, true);

        if (participant.nameDiv)
            participant.nameDiv.style.background = top ? `linear-gradient(${secondColor}, ${firstColor})` : `linear-gradient(${firstColor}, ${secondColor})`;

        if (participant.cursorDiv)
            participant.cursorDiv.style.background = top ? `linear-gradient(${secondColor}, ${firstColor})` : `linear-gradient(${firstColor}, ${secondColor})`;
    });

    function _sc_createButtons() {
        $("#rename p .text")[0].style.marginBottom = "5px";

        const br = document.createElement("br");
        $("#rename p .text")[0].after(br);

        const button = document.createElement("input");

        const secondColor = (window.localStorage.getItem("secondColor") === null || _sc_isColorInvalid(window.localStorage.getItem("secondColor")))
        ? MPP.client.getOwnParticipant().color : window.localStorage.getItem("secondColor");

        button.setAttribute("type", "color");
        button.setAttribute("class", "color");
        button.setAttribute("placeholder", "");
        button.setAttribute("value", secondColor);
        button.setAttribute("maxlength", 7);
        button.setAttribute("name", "secondcolor");

        const _button = document.createElement("button");

        _button.innerText = "Invert";
        _button.setAttribute("class", "ugly-button");
        _button.setAttribute("id", "invert");
        _button.style = "margin-left: 10px; width: 50px; height: 27px; user-select: none; color: white";

        _button.onclick = () => {
            let firstColor = $("#rename p .color")[0].value;
            $("#rename p .color")[0].value = button.value;
            button.value = firstColor;
        }

        const label = document.createElement("label");

        label.innerText = ' Second color at top: ';
        label.style.fontSize = '16px';
        label.setAttribute("id", "scattop");

        const checkbox = document.createElement("input");

        checkbox.type = 'checkbox';
        checkbox.id = 'second-color-at-top';
        checkbox.checked = currentAtTop;

        label.appendChild(checkbox);

        $("#rename p").append(button).append(_button).append(label);

        const _br = document.createElement("br");
        $("#rename p #scattop")[0].before(_br);

        const bottomButton = document.createElement("div");

        bottomButton.setAttribute("class", "ugly-button sclist-btn");
        bottomButton.setAttribute("id", "sclist-btn");

        bottomButton.innerText = "SC Users";

        bottomButton.onclick = _sc_logUsers;

        $("#bottom .relative .buttons")[0].appendChild(bottomButton);
    }

    _sc_createButtons();

    setTimeout(() => {
        fetch(new Request("https://aod.undo.it/misc/scv")).then(response => response.text().then(text => {
            const _sc_actualVersion = text.trim();
            if (_sc_version.indexOf(_sc_actualVersion) < 0) {
                new MPP.Notification({
                    title: "Your Second Color version is outdated!",
                    text: `You have ${_sc_version} while the latest is ${_sc_actualVersion}! Please get the new version from: https://greasyfork.org/en/users/1085754-mpp-firefox`, target: "#piano", duration: 10000
                });
            }
        }));

    }, 3000);

    console.log(`MPP Second Color ${_sc_version} loaded!`);

    new MPP.Notification({
        title: "Second Color",
        text: `MPP Second Color ${_sc_version} loaded!`, target: "#piano", duration: 3000
    });
};

function _sc_mppExists() {
    if (window.MPP === undefined) requestAnimationFrame(_sc_mppExists); else _sc_init();
}

requestAnimationFrame(_sc_mppExists);