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