Jump to content

User:Opencooper/IPtoEmoji.js

From 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});	
}