From fa335564eda13e793403912740f3d47c80867ccb Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 14 Sep 2017 00:10:29 +0700 Subject: [PATCH] Generate RegExp from phrases --- .../src/components/CommentBodyHighlighter.js | 100 ++++-------------- 1 file changed, 21 insertions(+), 79 deletions(-) diff --git a/client/coral-admin/src/components/CommentBodyHighlighter.js b/client/coral-admin/src/components/CommentBodyHighlighter.js index fe5bcff96..64b53b829 100644 --- a/client/coral-admin/src/components/CommentBodyHighlighter.js +++ b/client/coral-admin/src/components/CommentBodyHighlighter.js @@ -1,90 +1,32 @@ import React from 'react'; import {matchLinks} from '../utils'; -const capturingWordSeparator = /([.\s'"?!])/; -const wordSeparator = /[.\s'"?!]/; +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +function generateRegExp(phrases) { + const inner = phrases + .map((phrase) => { + return phrase.split(/\s+/) + .map((word) => escapeRegExp(word)) + .join('[\\s"?!.]+'); + }).join('|'); + + return `(^|[^\\w])(${inner})(?=[^\\w]|$)`; +} // markPhrases looks for `phrases` inside `body` and highlights them by returning // an array of React Elements. function markPhrases(body, phrases, keyPrefix) { - const tokens = body.split(capturingWordSeparator); - const phraseWords = phrases.map((phrase) => phrase.toLowerCase().split(wordSeparator)); - const content = []; - let tmp = []; - - for (let l = 0; l < tokens.length; l++) { - - // matchedWords is > 0 when a full match was found and contains - // the range length from this index to the end of the match. - let matchedWords = 0; - - // Skip word separators and ''. - if (tokens[l] !== '' && !tokens[l].match(wordSeparator)) { - for (let m = 0; m < phraseWords.length; m++) { - const words = phraseWords[m]; - - // We try to match the full phrase, index keeps track - // of where we are now on the tokens array while matching - // the words of the phrase. - let index = l; - for (let n = 0; n < words.length; n++, index++) { - - // Skip word separators and ''. - while (index < tokens.length && (tokens[index].match(wordSeparator) || tokens[index] === '')) { - index++; - } - - // No more tokens left. - if (index >= tokens.length) { - break; - } - - const token = tokens[index].toLowerCase(); - const word = words[n]; - if (token !== word) { - break; - } - - // Full match! - if (n === words.length - 1) { - - // Save the matched range length into matched words. - matchedWords = index - l + 1; - break; - } - } - - // We matched a word so break out the loop. - if (matchedWords) { - break; - } - } + const regexp = new RegExp(generateRegExp(phrases), 'iu'); + const tokens = body.split(regexp); + return tokens.map((token, i) => { + if (i % 3 === 2) { + return {token}; } - - // We have a match! - if (matchedWords) { - const match = tokens.slice(l, l + matchedWords).join(''); - - // Append whatever we have in `tmp` and clear it. - content.push(tmp.join('')); - tmp = []; - - content.push({match}); - - // Move index further if we matched more than one word. - l += matchedWords - 1; - - continue; - } - - // No match, we just push this into `tmp`. - tmp.push(tokens[l]); - } - - // Append any non matched tokens currently in `tmp`. - content.push(tmp.join('')); - - return content; + return token; + }); } // markLinks looks for links inside `body` and highlights them by returning