User:Opencooper/IPtoEmoji.js

Source: Wikipedia, the free encyclopedia.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// Converts IP addresses to emoji for better recognizability

// Uses a lookup table of 256 arbitrary emoji and uses the 8-bit hex parts of the
// address to pick the emoji. For IPv6, 16-bit hextets are split into two bytes.

// Test page: https://regex101.com/r/h9vsbC/1

// Interesting IPs: 2804:14D:5C59:8300:0:0:0:1000
// I could've sworn there was a registered user whose username was in the format of an actual IP

// License: CC0

// TODO: Don't run when editing
// Consider: Add condensed IPv6 emojis and let users toggle via CSS?
// FIXME: https://en.wikipedia.org/wiki/List_of_EC_numbers_(EC_3)

// Stolen from: https://gist.github.com/windytan/7910910/
var emojiTable = [ "🌀", "🌂", "🌅", "🌈", "🌙", "🌞", "🌟", "🌠", "🌰", "🌱", "🌲", "🌳", "🌴", "🌵", "🌷", "🌸", "🌹", "🌺", "🌻", "🌼", "🌽", "🌾", "🌿", "🍀", "🍁", "🍂", "🍃", "🍄", "🍅", "🍆", "🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🍎", "🍏", "🍐", "🍑", "🍒", "🍓", "🍔", "🍕", "🍖", "🍗", "🍘", "🍜", "🍝", "🍞", "🍟", "🍠", "🍡", "🍢", "🍣", "🍤", "🍥", "🍦", "🍧", "🍨", "🍩", "🍪", "🍫", "🍬", "🍭", "🍮", "🍯", "🍰", "🍱", "🍲", "🍳", "🍴", "🍵", "🍶", "🍷", "🍸", "🍹", "🍺", "🍻", "🍼", "🎀", "🎁", "🎂", "🎃", "🎄", "🎅", "🎈", "🎉", "🎊", "🎋", "🎌", "🎍", "🎎", "🎏", "🎒", "🎓", "🎠", "🎡", "🎢", "🎣", "🎤", "🎥", "🎦", "🎧", "🎨", "🎩", "🎪", "🎫", "🎬", "🎭", "🎮", "🎯", "🎰", "🎱", "🎲", "🎳", "🎴", "🎵", "🎷", "🎸", "🎹", "🎺", "🎻", "🎽", "🎾", "🎿", "🏀", "🏁", "🏂", "🏃", "🏄", "🏆", "🏇", "🏈", "🏉", "🏊", "🐀", "🐁", "🐂", "🐃", "🐄", "🐅", "🐆", "🐇", "🐈", "🐉", "🐊", "🐋", "🐌", "🐍", "🐎", "🐏", "🐐", "🐑", "🐒", "🐓", "🐔", "🐕", "🐖", "🐗", "🐘", "🐙", "🐚", "🐛", "🐜", "🐝", "🐞", "🐟", "🐠", "🐡", "🐢", "🐣", "🐤", "🐥", "🐦", "🐧", "🐨", "🐩", "🐪", "🐫", "🐬", "🐭", "🐮", "🐯", "🐰", "🐱", "🐲", "🐳", "🐴", "🐵", "🐶", "🐷", "🐸", "🐹", "🐺", "🐻", "🐼", "🐽", "🐾", "👀", "👂", "👃", "👄", "👅", "👆", "👇", "👈", "👉", "👊", "👋", "👌", "👍", "👎", "👏", "👐", "👑", "👒", "👓", "👔", "👕", "👖", "👗", "👘", "👙", "👚", "👛", "👜", "👝", "👞", "👟", "👠", "👡", "👢", "👣", "👤", "👥", "👦", "👧", "👨", "👩", "👪", "👮", "👯", "👺", "👻", "👼", "👽", "👾", "👿", "💀", "💁", "💂", "💃", "💄", "💅" ];
// Pretty rudimentary, but passable since we only use it in restricted circumstances
// Chokes on "User:[IP]" for IPv6
var IPregex = /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[0-9A-F]*:[0-9A-F]*:[0-9A-F]*:[0-9A-F]*:[0-9A-F]*:[0-9A-F]*:[0-9A-F]*:?[0-9A-F]*)/gi;

function wrapEmoji(emoji) {
	return "<span class='IP-emoji' title='IP As Emoji' dir='ltr'> [" + emoji + "]</span>";
}

function addHrefEmoji() {
	var text = $(this).text();
	var href = $(this).attr("href");
	var match = text.match(IPregex);
	if (match && match.length == 1 && IPregex.test(href)) {
		var IP = match[0];
		var emoji = convertIP(IP);
		if (!emoji) {return;}
		var emojiMarkup = wrapEmoji(emoji);
		// We don't want to change the link text because that's what
		// Twinkle.fluff.revert uses when reverting
		$(this).after(emojiMarkup);
	}
}

function addContribEmoji() {
	var text = $(".mw-contributions-user-tools").text();
	var match = text.match(IPregex);
	if (match) {
		var IP = match[0];
		var emoji = convertIP(IP);
		if (!emoji) {return;}
		var emojiMarkup = wrapEmoji(emoji);
		$("#contentSub").append(emojiMarkup);
	}
}

function addUserTalkEmoji() {
	var text = mw.config.get("wgTitle");
	var match = text.match(IPregex);
	if (match) {
		var IP = match[0];
		var emoji = convertIP(IP);
		if (!emoji) {return;}
		var emojiMarkup = wrapEmoji(emoji);
		$("h1").append(emojiMarkup);
	}
}

function convertIP(IP) {
	var base;
	var conversion = "";
	var groups = [];

	if (/\./.test(IP)) { // IPv4
		base = 10;
		groups = IP.split(".");
		
		if (groups.length != 4) {
			return;
		}
	} else if (/:/.test(IP)) { // IPv6
		base = 16;
		// Sometimes two consecutive zero-groups are omitted with "::"
		IP = IP.replace(/::/g, ":0:0:");

		v6groups = IP.split(":");
		if (v6groups.length != 8) {
			return;
		}
		
		// Since IPv6 has 2-byte groups, we need to split those bytes
		for (let i=0; i<v6groups.length; i++) {
			var v6group = v6groups[i];
			if (v6group.length < 4) {
				v6group = v6group.padStart(4, "0");
			}
			groups.push(v6group.slice(0, 2));
			groups.push(v6group.slice(2, 4));
		}
	} else {
		console.error("User:Opencooper/IPtoEmoji.js: " + IP + " was not readable as an IP address");
		return;
	}
	
	for (let i=0; i<groups.length; i++) {
		if (groups[i] < 0 || groups[i] > 255) {
			return;
		}

		var index = parseInt(groups[i], base); 
		conversion += emojiTable[index];
	}

	return conversion;
}

// Don't italicize in history pages
var sheet = window.document.styleSheets[0];
sheet.insertRule('.IP-emoji { font-style: normal; }');

// Contributions pages
if ($(".mw-contributions-list").length) {
	addContribEmoji();
}

// User talk pages and user pages
if (mw.config.get('wgNamespaceNumber') == 3 || mw.config.get('wgNamespaceNumber') == 2) {
	addUserTalkEmoji();
}

// Links to contrib pages
if (mw.config.get("wgPageName") != "Special:Watchlist"
    && mw.config.get("wgPageName") != "Special:RecentChanges") {
	$("#mw-content-text a").each(addHrefEmoji);
	
	// Cleanup references erroneously recognized as IPs
	$(".reflist .IP-emoji").remove();
}

// Cleanup from TOC
$("#toc .IP-emoji").remove();

// Update on Watchlist and Recent Changes pages
if ($(".mw-changeslist").length) {
	var target = document.querySelector(".mw-changeslist");
	var observer = new MutationObserver(function(mutations) {
	    $("#mw-content-text a").each(addHrefEmoji);
	});

	observer.observe(target, {childList: true});	
}