import { makeArray } from "discourse-common/lib/helpers"; function highlight(node, pattern, nodeName, className) { if ( ![Node.ELEMENT_NODE, Node.TEXT_NODE].includes(node.nodeType) || ["SCRIPT", "STYLE"].includes(node.tagName) || (node.tagName === nodeName && node.className === className) ) { return 0; } if (node.nodeType === Node.ELEMENT_NODE && node.childNodes) { for (let i = 0; i < node.childNodes.length; i++) { i += highlight(node.childNodes[i], pattern, nodeName, className); } return 0; } if (node.nodeType === Node.TEXT_NODE) { const match = node.data.match(pattern); if (!match) { return 0; } const element = document.createElement(nodeName); element.className = className; element.innerText = match[0]; const matchNode = node.splitText(match.index); matchNode.splitText(match[0].length); matchNode.parentNode.replaceChild(element, matchNode); return 1; } return 0; } export default function(node, words, opts = {}) { let settings = { nodeName: "span", className: "highlighted", matchCase: false }; settings = Object.assign({}, settings, opts); words = makeArray(words) .filter(Boolean) .map(word => word.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")); if (!words.length) return node; const pattern = `(${words.join(" ")})`; let flag; if (!settings.matchCase) { flag = "i"; } highlight( node, new RegExp(pattern, flag), settings.nodeName.toUpperCase(), settings.className ); return node; } export function unhighlightHTML(opts = {}) { let settings = { nodeName: "span", className: "highlighted" }; settings = Object.assign({}, settings, opts); document .querySelectorAll(`${settings.nodeName}.${settings.className}`) .forEach(element => { const parentNode = element.parentNode; parentNode.replaceChild(element.firstChild, element); parentNode.normalize(); }); }