UNPKG

yoastseo

Version:

Yoast client-side content analysis

189 lines (176 loc) 8.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.deConstructAnchor = exports.collectMarkingsInSentence = void 0; exports.markWordsInASentence = markWordsInASentence; exports.markWordsInSentences = markWordsInSentences; exports.reConstructAnchor = void 0; var _matchTextWithArray = _interopRequireDefault(require("../match/matchTextWithArray")); var _createRegexFromArray = _interopRequireDefault(require("../regex/createRegexFromArray")); var _addMarkSingleWord = _interopRequireDefault(require("../../../markers/addMarkSingleWord")); var _Mark = _interopRequireDefault(require("../../../values/Mark")); var _lodash = require("lodash"); var _getAnchorsFromText = _interopRequireDefault(require("../link/getAnchorsFromText")); var _quotes = require("../sanitize/quotes"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // Regex to deconstruct an anchor into open tag, content and close tag. const anchorDeconstructionRegex = /(<a[\s]+[^>]+>)([^]*?)(<\/a>)/; /** * Deconstructs an anchor to the opening tag and the content. The content is the anchor text. * We don't return the closing tag since the value would always be the same, i.e. </a>. * * @param {string} anchor An anchor of the shape <a ...>...</a>. * * @returns {object} An object containing the opening tag and the content. */ const deConstructAnchor = function (anchor) { // The const array mirrors the anchorDeconstructionRegex, using a comma to access the first element without a name. const [, openTag, content] = anchor.match(anchorDeconstructionRegex); return { openTag: openTag, content: content }; }; /** * Reconstructs an anchor from an openTag, the content, and the closing tag. * * @param {string} openTag The opening tag of the anchor. Must be of the shape <a ...>. * @param {string} content The text of the anchor. * * @returns {string} An anchor. */ exports.deConstructAnchor = deConstructAnchor; const reConstructAnchor = function (openTag, content) { return `${openTag}${content}</a>`; }; /** * Gets the anchors and marks the anchors' text if the words are found in it. * * @param {string} sentence The sentence to retrieve the anchors from. * @param {RegExp} wordsRegex The regex of the words. * * @returns {Object} The anchors and the marked anchors. */ exports.reConstructAnchor = reConstructAnchor; const getMarkedAnchors = function (sentence, wordsRegex) { // Retrieve the anchors. const anchors = (0, _getAnchorsFromText.default)(sentence); // For every anchor, apply the markings only to the anchor tag. const markedAnchors = anchors.map(anchor => { // Retrieve the open tag and the content/anchor text. const { openTag, content } = deConstructAnchor(anchor); // Apply the marking to the anchor text if there is a match. const markedAnchorText = content.replace(wordsRegex, x => (0, _addMarkSingleWord.default)(x)); // Create a new anchor tag with a (marked) anchor text. return reConstructAnchor(openTag, markedAnchorText); }); return { anchors, markedAnchors }; }; /** * Adds marks to a sentence and merges marks if those are only separated by a space * (e.g., if highlighting words "ballet" and "shoes" in a sentence "I have a lot of ballet shoes and other paraphernalia." * the marks will be put around "ballet shoes" together, not "`ballet` `shoes`".) * * @param {string} sentence The sentence to mark words in. * @param {[string]} wordsFoundInSentence The words to mark in the sentence. * @param {function} matchWordCustomHelper The language-specific helper function to match word in text. * * @returns {string} The sentence with marks. */ const collectMarkingsInSentence = function (sentence, wordsFoundInSentence, matchWordCustomHelper) { const allWordsFound = []; wordsFoundInSentence.forEach(word => { // Check if the word in `wordsFoundInSentence` contains single quote(s). const matchedSingleQuotes = word.match(_quotes.SINGLE_QUOTES_REGEX); if (matchedSingleQuotes) { /* * If yes, make all different combinations of the word with different types of single quotes in the array. * Later, a regex will be created for all words that were found in the sentence including their variations. * * For example: * `wordsFoundInSentence`: [ "red", "panda’s" ] * For the word "panda’s", we'll create the following variations: * "panda's", "panda‘s", "panda’s", "panda‛s", "panda`s", "panda‹s", "panda›s" * And those variations will be added to `allWordsFound`. */ _quotes.SINGLE_QUOTES_ARRAY.forEach(singleQuote => { matchedSingleQuotes.forEach(matchedSingleQuote => { allWordsFound.push((0, _lodash.escapeRegExp)(word.replace(new RegExp(matchedSingleQuote, "g"), singleQuote))); }); }); } else { allWordsFound.push((0, _lodash.escapeRegExp)(word)); } }); // If a language has a custom helper to match words, we disable the word boundary when creating the regex. const wordsRegex = matchWordCustomHelper ? (0, _createRegexFromArray.default)(allWordsFound, true) : (0, _createRegexFromArray.default)(allWordsFound); // Retrieve the anchors and mark the anchors' text if the words are found in the anchors' text. const { anchors, markedAnchors } = getMarkedAnchors(sentence, wordsRegex); let markup = sentence.replace(wordsRegex, function (x) { return (0, _addMarkSingleWord.default)(x); }); /** * In 'markup', we apply the markings also inside the anchor's attribute if there is a match, on top of * marking the anchor's text. * The step below is to replace the incorrectly marked anchors with the marked anchors that we want: * where the markings are only applied in the anchor's text. */ if (anchors.length > 0) { const markupAnchors = (0, _getAnchorsFromText.default)(markup); for (let i = 0; i < markupAnchors.length; i++) { markup = markup.replace(markupAnchors[i], markedAnchors[i]); } } /* * If two marks are separated by only a space, remove the closing tag of the first mark and the opening tag of the * second mark so that the two marks can be combined into one. */ return markup.replace(new RegExp("</yoastmark> <yoastmark class='yoast-text-mark'>", "ig"), " "); }; /** * Adds marks to a sentence. * * @param {string} sentence The sentence in which we want to apply highlighting. * @param {Array} wordsFoundInSentence The words to highlight in a sentence. * @param {function} matchWordCustomHelper The language-specific helper function to match word in text. * @returns {Mark[]} The array of Mark objects of each sentence. */ exports.collectMarkingsInSentence = collectMarkingsInSentence; function markWordsInASentence(sentence, wordsFoundInSentence, matchWordCustomHelper) { return [new _Mark.default({ original: sentence, marked: collectMarkingsInSentence(sentence, wordsFoundInSentence, matchWordCustomHelper) })]; } /** * Adds marks to an array of sentences. * * @param {[string]} wordsToMark The words to mark. * @param {[string]} sentences The sentences in which to mark these words. * @param {string} locale The locale. * @param {function} matchWordCustomHelper The language-specific helper function to match word in text. * * @returns {[string]} The sentences with marks. */ function markWordsInSentences(wordsToMark, sentences, locale, matchWordCustomHelper) { let wordsFoundInSentence = []; let markings = []; sentences.forEach(function (sentence) { wordsFoundInSentence = (0, _matchTextWithArray.default)(sentence, wordsToMark, locale, matchWordCustomHelper).matches; if (wordsFoundInSentence.length > 0) { markings = markings.concat(markWordsInASentence(sentence, wordsFoundInSentence, matchWordCustomHelper)); } }); return markings; } //# sourceMappingURL=markWordsInSentences.js.map