Greasy Fork is available in English.

WhatsApp Web Country Flags

Inserts flag images in front of phone numbers not saved as contact.

// ==UserScript==
// @name         WhatsApp Web Country Flags
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @description  Inserts flag images in front of phone numbers not saved as contact.
// @author       Tobias Deißler
// @match        https://web.whatsapp.com/
// @grant        none
// ==/UserScript==

"use strict";

const callingCodeToCountryTable =
{
    "886": "tw",
    "93": "af",
    "355": "al",
    "213": "dz",
    "1 (684)": "as",
    "376": "ad",
    "244": "ao",
    "1 (264)": "ai",
    "672": "aq",
    "1 (268)": "ag",
    "54": "ar",
    "374": "am",
    "297": "aw",
    "61": "au",
    "43": "at",
    "994": "az",
    "1 (242)": "bs",
    "973": "bh",
    "880": "bd",
    "1 (246)": "bb",
    "375": "by",
    "32": "be",
    "501": "bz",
    "229": "bj",
    "1 (441)": "bm",
    "975": "bt",
    "591": "bo",
    "599": "bq",
    "387": "ba",
    "267": "bw",
    "47": "bv",
    "55": "br",
    "246": "io",
    "1 (284)": "vg",
    "673": "bn",
    "359": "bg",
    "226": "bf",
    "257": "bi",
    "238": "cv",
    "855": "kh",
    "237": "cm",
    "1": "ca",
    "1 (345)": "ky",
    "236": "cf",
    "235": "td",
    "56": "cl",
    "86": "cn",
    "852": "hk",
    "853": "mo",
    "61 (891)": "cc",
    "57": "co",
    "269": "km",
    "242": "cg",
    "682": "ck",
    "506": "cr",
    "385": "hr",
    "53": "cu",
    "599": "cw",
    "357": "cy",
    "420": "cz",
    "225": "ci",
    "850": "kp",
    "243": "cd",
    "45": "dk",
    "253": "dj",
    "1 (767)": "dm",
    "1 (809)": "do",
    "1 (829)": "do",
    "1 (849)": "do",
    "593": "ec",
    "20": "eg",
    "503": "sv",
    "240": "gq",
    "291": "er",
    "372": "ee",
    "251": "et",
    "500": "fk",
    "298": "fo",
    "679": "fj",
    "358": "fi",
    "33": "fr",
    "594": "gf",
    "689": "pf",
    "262": "tf",
    "241": "ga",
    "220": "gm",
    "995": "ge",
    "49": "de",
    "233": "gh",
    "350": "gi",
    "30": "gr",
    "299": "gl",
    "1 (473)": "gd",
    "590": "gp",
    "1 (671)": "gu",
    "502": "gt",
    "44": "gg",
    "224": "gn",
    "245": "gw",
    "592": "gy",
    "509": "ht",
    "672": "hm",
    "39 (06)": "va",
    "504": "hn",
    "36": "hu",
    "354": "is",
    "91": "in",
    "62": "id",
    "98": "ir",
    "964": "iq",
    "353": "ie",
    "44": "im",
    "972": "il",
    "39": "it",
    "1 (876)": "jm",
    "81": "jp",
    "44": "je",
    "962": "jo",
    "7": "kz",
    "254": "ke",
    "686": "ki",
    "965": "kw",
    "996": "kg",
    "856": "la",
    "371": "lv",
    "961": "lb",
    "266": "ls",
    "231": "lr",
    "218": "ly",
    "423": "li",
    "370": "lt",
    "352": "lu",
    "261": "mg",
    "265": "mw",
    "60": "my",
    "960": "mv",
    "223": "ml",
    "356": "mt",
    "692": "mh",
    "596": "mq",
    "222": "mr",
    "230": "mu",
    "262": "yt",
    "52": "mx",
    "691": "fm",
    "377": "mc",
    "976": "mn",
    "382": "me",
    "1 (664)": "ms",
    "212": "ma",
    "258": "mz",
    "95": "mm",
    "264": "na",
    "674": "nr",
    "977": "np",
    "31": "nl",
    "687": "nc",
    "64": "nz",
    "505": "ni",
    "227": "ne",
    "234": "ng",
    "683": "nu",
    "672": "nf",
    "1 (670)": "mp",
    "47": "no",
    "968": "om",
    "92": "pk",
    "680": "pw",
    "507": "pa",
    "675": "pg",
    "595": "py",
    "51": "pe",
    "63": "ph",
    "870": "pn",
    "48": "pl",
    "351": "pt",
    "1": "pr",
    "974": "qa",
    "82": "kr",
    "373": "md",
    "40": "ro",
    "7": "ru",
    "250": "rw",
    "262": "re",
    "590": "bl",
    "290": "sh",
    "1 (869)": "kn",
    "1 (758)": "lc",
    "590": "mf",
    "508": "pm",
    "1 (784)": "vc",
    "685": "ws",
    "378": "sm",
    "239": "st",
    "966": "sa",
    "221": "sn",
    "381": "rs",
    "248": "sc",
    "232": "sl",
    "65": "sg",
    "1 (721)": "sx",
    "421": "sk",
    "386": "si",
    "677": "sb",
    "252": "so",
    "27": "za",
    "500": "gs",
    "211": "ss",
    "34": "es",
    "94": "lk",
    "970": "ps",
    "249": "sd",
    "597": "sr",
    "268": "sz",
    "46": "se",
    "41": "ch",
    "963": "sy",
    "992": "tj",
    "66": "th",
    "389": "mk",
    "670": "tl",
    "228": "tg",
    "690": "tk",
    "676": "to",
    "1 (868)": "tt",
    "216": "tn",
    "90": "tr",
    "993": "tm",
    "1 (649)": "tc",
    "688": "tv",
    "256": "ug",
    "380": "ua",
    "971": "ae",
    "44": "gb",
    "255": "tz",
    "1 (340)": "vi",
    "1": "us",
    "598": "uy",
    "998": "uz",
    "678": "vu",
    "58": "ve",
    "84": "vn",
    "681": "wf",
    "212": "eh",
    "967": "ye",
    "260": "zm",
    "263": "zw",
    "358": "ax"
};

const flagImagesFolderPath = "https://raw.githubusercontent.com/todeit02/whatsapp_web_country_flags_userscript/master/flags";
const phoneNumberChatSelector = ".WJuYU";
const initialInsertId = "main";
const insertingFlagsClass = "phoneNumberCountryFlag";

debugger;
const messageInsertObserver = new MutationObserver(insertFlags);
const observerOptions = { childList: true, subtree: true };
messageInsertObserver.observe(document, observerOptions);


function insertFlags(mutationRecords)
{
    const addedPhoneNumberNodes = extractPhoneNumberNodes(mutationRecords);
    for(let i = 0; i < addedPhoneNumberNodes.length; i++)
    {
        const alreadyHasFlag = Array.from(addedPhoneNumberNodes[i].parentElement.children).some(nodeSibling => nodeSibling.classList.contains(insertingFlagsClass));
        if(!alreadyHasFlag)
        {
            insertFlag(addedPhoneNumberNodes[i]);
        }
    }
};


function insertFlag(phoneNumberNode)
{
    const phoneNumber = phoneNumberNode.textContent;
    
    for(let testingCallingCode of Object.keys(callingCodeToCountryTable))
    {
        // bug: possible longer match
        if(phoneNumber.startsWith('+' + testingCallingCode))
        {
            const containedCountryFlag = createFlagImageInContainerNode(callingCodeToCountryTable[testingCallingCode]);    
            phoneNumberNode.parentElement.insertBefore(containedCountryFlag, phoneNumberNode);
            break;
        }
    }
}


function createFlagImageInContainerNode(iso31661alpha2countryCode)
{
    const countryFlagContainer = document.createElement("div");
    countryFlagContainer.style.display = "flex";
    countryFlagContainer.style.alignItems = "center";

    const countryFlag = new Image();
    countryFlag.classList.add(insertingFlagsClass);
    countryFlag.style.marginRight = "0.4em";
    countryFlag.style.height = "1em";    
    countryFlag.src = flagImagesFolderPath + '/' + iso31661alpha2countryCode + ".png";
    countryFlag.title = iso31661alpha2countryCode;

    countryFlagContainer.appendChild(countryFlag);

    return countryFlagContainer;
}


function extractPhoneNumberNodes(mutationRecords)
{
    const phoneNumberNodes = [];

    mutationRecords.forEach(mutationRecord =>
    {
        mutationRecord.addedNodes.forEach(addedNode =>
        {
            const shouldContainPhoneNumberNodes = addedNode.getAttribute('role') === 'row' || addedNode.id === initialInsertId;
            if(!shouldContainPhoneNumberNodes) return;

            const phoneNumberNodesInElement = addedNode.querySelectorAll(phoneNumberChatSelector);
            phoneNumberNodes.push(...phoneNumberNodesInElement);
        });
    });

    return phoneNumberNodes;
}