varkor / WhatsApp Emoticon Preserver

// ==UserScript==
// @name			WhatsApp Emoticon Preserver
// @namespace		https://gist.github.com/varkor/ca697f6fd59f60b5b9a8aeaa6d7cb341
// @version			0.5
// @author			varkor
// @description		Disable automatic emoticon → emoji conversion in WhatsApp Web
// @match			https://web.whatsapp.com/
// @grant			none
// ==/UserScript==

(() => {
	"use strict";
	if (window.location.hostname !== "web.whatsapp.com") {
		window.location = "https://web.whatsapp.com/";
	} else {
		const fixed = new Set();
		const fix = function () {
			const input = document.querySelector('[data-tab="1"]');
			if (input && !fixed.has(input)) {
				const volatile = new RegExp(["(y)", "(n)", ":-)", ":)", ":-(", ":(", ":-p", ":p", ":-|", ":|", ":-\\", ":-d", ":d", ":-*", "<3", "^_^", ">_<", ";-)"].map((string) => string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")).join("|"), "gi");
				input.addEventListener("input", (event) => {
					const selection = window.getSelection();
					let containers = null;
					let start = null;
					let end = null;
					if (selection.rangeCount >= 1) {
						const range = selection.getRangeAt(0);
						containers = [range.startContainer, range.endContainer];
						start = range.startOffset;
						end = range.endOffset;
					}
					const walker = document.createTreeWalker(input, NodeFilter.SHOW_TEXT);
					let node;
					while (node = walker.nextNode()) {
						node.nodeValue = node.nodeValue.replace(volatile, (match, offset) => {
							if (containers !== null) {
								if (containers[0] === node && start > offset) {
									start += Math.min(match.length - 1, start - offset);
								}
								if (containers[1] === node && end > offset) {
									end += Math.min(match.length - 1, end - offset);
								}
							}
							return match.split("").join("\u200B");
						});
					}
					if (containers !== null) {
						selection.removeAllRanges();
						const range = document.createRange();
						range.setStart(containers[0], start);
						range.setEnd(containers[1], end);
						selection.addRange(range);
					}
				});
				fixed.add(input);
			}
		};
		window.addEventListener("click", fix);
		window.addEventListener("paste", () => setTimeout(fix, 400));
		fix();
	}
})();