mirror of
https://github.com/mdn/webextensions-examples.git
synced 2026-04-16 06:18:35 +02:00
96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
/*
|
|
* This file is responsible for performing the logic of replacing
|
|
* all occurrences of each mapped word with its emoji counterpart.
|
|
*/
|
|
|
|
/*global sortedEmojiMap*/
|
|
|
|
// emojiMap.js defines the 'sortedEmojiMap' variable.
|
|
// Referenced here to reduce confusion.
|
|
const emojiMap = sortedEmojiMap;
|
|
|
|
/*
|
|
* For efficiency, create a word --> search RegEx Map too.
|
|
*/
|
|
let regexs = new Map();
|
|
for (let word of emojiMap.keys()) {
|
|
// We want a global, case-insensitive replacement.
|
|
// @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
|
|
regexs.set(word, new RegExp(word, 'gi'));
|
|
}
|
|
|
|
/**
|
|
* Substitutes emojis into text nodes.
|
|
* If the node contains more than just text (ex: it has child nodes),
|
|
* call replaceText() on each of its children.
|
|
*
|
|
* @param {Node} node - The target DOM Node.
|
|
* @return {void} - Note: the emoji substitution is done inline.
|
|
*/
|
|
function replaceText (node) {
|
|
// Setting textContent on a node removes all of its children and replaces
|
|
// them with a single text node. Since we don't want to alter the DOM aside
|
|
// from substituting text, we only substitute on single text nodes.
|
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
|
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
// This node only contains text.
|
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType.
|
|
|
|
// Skip textarea nodes due to the potential for accidental submission
|
|
// of substituted emoji where none was intended.
|
|
if (node.parentNode &&
|
|
node.parentNode.nodeName === 'TEXTAREA') {
|
|
return;
|
|
}
|
|
|
|
// Because DOM manipulation is slow, we don't want to keep setting
|
|
// textContent after every replacement. Instead, manipulate a copy of
|
|
// this string outside of the DOM and then perform the manipulation
|
|
// once, at the end.
|
|
let content = node.textContent;
|
|
|
|
// Replace every occurrence of 'word' in 'content' with its emoji.
|
|
// Use the emojiMap for replacements.
|
|
for (let [word, emoji] of emojiMap) {
|
|
// Grab the search regex for this word.
|
|
const regex = regexs.get(word);
|
|
|
|
// Actually do the replacement / substitution.
|
|
// Note: if 'word' does not appear in 'content', nothing happens.
|
|
content = content.replace(regex, emoji);
|
|
}
|
|
|
|
// Now that all the replacements are done, perform the DOM manipulation.
|
|
node.textContent = content;
|
|
}
|
|
else {
|
|
// This node contains more than just text, call replaceText() on each
|
|
// of its children.
|
|
for (let i = 0; i < node.childNodes.length; i++) {
|
|
replaceText(node.childNodes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start the recursion from the body tag.
|
|
replaceText(document.body);
|
|
|
|
// Now monitor the DOM for additions and substitute emoji into new nodes.
|
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver.
|
|
const observer = new MutationObserver((mutations) => {
|
|
mutations.forEach((mutation) => {
|
|
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
|
|
// This DOM change was new nodes being added. Run our substitution
|
|
// algorithm on each newly added node.
|
|
for (let i = 0; i < mutation.addedNodes.length; i++) {
|
|
const newNode = mutation.addedNodes[i];
|
|
replaceText(newNode);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
observer.observe(document.body, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|