FIX: remove word boundary regex (\b) for search result highlights. (#9338)

FIX: remove word boundary regex (\b) for search result highlights. (#9338)

diff --git a/app/assets/javascripts/admin/components/site-text-summary.js b/app/assets/javascripts/admin/components/site-text-summary.js
index 11c6bc4..23ee2d8 100644
--- a/app/assets/javascripts/admin/components/site-text-summary.js
+++ b/app/assets/javascripts/admin/components/site-text-summary.js
@@ -1,5 +1,6 @@
 import Component from "@ember/component";
 import { on } from "discourse-common/utils/decorators";
+import highlightHTML from "discourse/lib/highlight-html";
 
 export default Component.extend({
   classNames: ["site-text"],
@@ -10,11 +11,13 @@ export default Component.extend({
     const term = this._searchTerm();
 
     if (term) {
-      $(
-        this.element.querySelector(".site-text-id, .site-text-value")
-      ).highlight(term, {
-        className: "text-highlight"
-      });
+      highlightHTML(
+        this.element.querySelector(".site-text-id, .site-text-value"),
+        term,
+        {
+          className: "text-highlight"
+        }
+      );
     }
     $(this.element.querySelector(".site-text-value")).ellipsis();
   },
diff --git a/app/assets/javascripts/admin/templates/search-logs-term.hbs b/app/assets/javascripts/admin/templates/search-logs-term.hbs
index 5014fd7..54348ae 100644
--- a/app/assets/javascripts/admin/templates/search-logs-term.hbs
+++ b/app/assets/javascripts/admin/templates/search-logs-term.hbs
@@ -31,7 +31,7 @@
         <div class="fps-topic">
           <div class="topic">
             <a href={{result.url}} class="search-link">
-              {{topic-status topic=result.topic disableActions=true}}<span class="topic-title">{{#highlight-text highlight=term}}{{html-safe result.topic.fancyTitle}}{{/highlight-text}}</span>
+              {{topic-status topic=result.topic disableActions=true}}<span class="topic-title">{{#highlight-search highlight=term}}{{html-safe result.topic.fancyTitle}}{{/highlight-search}}</span>
             </a>
 
             <div class="search-category">
@@ -54,9 +54,9 @@
             </span>
 
             {{#if result.blurb}}
-              {{#highlight-text highlight=term}}
+              {{#highlight-search highlight=term}}
                 {{html-safe result.blurb}}
-              {{/highlight-text}}
+              {{/highlight-search}}
             {{/if}}
           </div>
         </div>
diff --git a/app/assets/javascripts/discourse/components/highlight-search.js b/app/assets/javascripts/discourse/components/highlight-search.js
new file mode 100644
index 0000000..a7077a7
--- /dev/null
+++ b/app/assets/javascripts/discourse/components/highlight-search.js
@@ -0,0 +1,13 @@
+import Component from "@ember/component";
+import highlightSearch from "discourse/lib/highlight-search";
+
+export default Component.extend({
+  tagName: "span",
+
+  _highlightOnInsert: function() {
+    const term = this.highlight;
+    highlightSearch(this.element, term);
+  }
+    .observes("highlight")
+    .on("didInsertElement")
+});
diff --git a/app/assets/javascripts/discourse/components/highlight-text.js b/app/assets/javascripts/discourse/components/highlight-text.js
index a98ffdb..d97572d 100644
--- a/app/assets/javascripts/discourse/components/highlight-text.js
+++ b/app/assets/javascripts/discourse/components/highlight-text.js
@@ -1,13 +1,11 @@
-import Component from "@ember/component";
-import highlightText from "discourse/lib/highlight-text";
+import highlightSearch from "discourse/components/highlight-search";
+import deprecated from "discourse-common/lib/deprecated";
 
-export default Component.extend({
-  tagName: "span",
-
-  _highlightOnInsert: function() {
-    const term = this.highlight;
-    highlightText($(this.element), term);
+export default highlightSearch.extend({
+  init() {
+    this._super(...arguments);
+    deprecated(
+      "`highlight-text` component is deprecated,  use the `highlight-search` instead."
+    );
   }
-    .observes("highlight")
-    .on("didInsertElement")
 });
diff --git a/app/assets/javascripts/discourse/lib/highlight-html.js b/app/assets/javascripts/discourse/lib/highlight-html.js
new file mode 100644
index 0000000..a6f093b
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/highlight-html.js
@@ -0,0 +1,84 @@
+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();
+    });
+}
diff --git a/app/assets/javascripts/discourse/lib/highlight-search.js b/app/assets/javascripts/discourse/lib/highlight-search.js
new file mode 100644
index 0000000..0d9318f
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/highlight-search.js
@@ -0,0 +1,19 @@
+import { PHRASE_MATCH_REGEXP_PATTERN } from "discourse/lib/concerns/search-constants";
+import highlightHTML from "discourse/lib/highlight-html";
+
+export const CLASS_NAME = "search-highlight";
+
+export default function(elem, term, opts = {}) {
+  if (!_.isEmpty(term)) {
+    // special case ignore "l" which is used for magic sorting
+    let words = _.reject(
+      term.match(new RegExp(`${PHRASE_MATCH_REGEXP_PATTERN}|[^\\s]+`, "g")),
+      t => t === "l"
+    );
+
+    words = words.map(w => w.replace(/^"(.*)"$/, "$1"));
+    const highlightOpts = {};
+    if (!opts.defaultClassName) highlightOpts.className = CLASS_NAME;
+    highlightHTML(elem, words, highlightOpts);
+  }
+}
diff --git a/app/assets/javascripts/discourse/lib/highlight-text.js b/app/assets/javascripts/discourse/lib/highlight-text.js
deleted file mode 100644
index 6fa7a09..0000000
--- a/app/assets/javascripts/discourse/lib/highlight-text.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PHRASE_MATCH_REGEXP_PATTERN } from "discourse/lib/concerns/search-constants";
-
-export const CLASS_NAME = "search-highlight";
-
-export default function($elem, term, opts = {}) {
-  if (!_.isEmpty(term)) {
-    // special case ignore "l" which is used for magic sorting
-    let words = _.reject(
-      term.match(new RegExp(`${PHRASE_MATCH_REGEXP_PATTERN}|[^\\s]+`, "g")),
-      t => t === "l"
-    );
-
-    words = words.map(w => w.replace(/^"(.*)"$/, "$1"));

[... diff too long, it was truncated ...]

GitHub sha: 4a2c4232

This commit appears in #9338 which was approved by jjaffeux and eviltrout. It was merged by vinothkannans.