UNPKG

yoastseo

Version:

Yoast client-side content analysis

147 lines (140 loc) 6.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _Mark = _interopRequireDefault(require("../../../values/Mark")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const markStart = "<yoastmark class='yoast-text-mark'>"; const markEnd = "</yoastmark>"; /** * Adds `yoastmark` tags to the keyphrase matches in the sentence. * * This is a helper for search-based highlighting. * * @param {Sentence} sentence The sentence to add the `yoastmark` tags to. * @param {Token[]} matches The array of the keyphrase matches. * * @returns {string} The sentence with the added `yoastmark` tags. */ const createMarksForSentence = (sentence, matches) => { const tokens = sentence.tokens; const newTokens = []; for (let i = tokens.length - 1; i >= 0; i--) { const token = tokens[i]; if (matches.some(match => match.sourceCodeRange.startOffset === token.sourceCodeRange.startOffset || match.sourceCodeRange.endOffset === token.sourceCodeRange.endOffset)) { newTokens.unshift(markStart, token.text, markEnd); } else { newTokens.unshift(token.text); } } const markedSentence = newTokens.join(""); // Merge consecutive markings into one marking. return markedSentence.replace(new RegExp("</yoastmark>([ \u00A0]?)<yoastmark class='yoast-text-mark'>", "ig"), "$1"); }; /** * Merges consecutive and overlapping markings into one marking. * * This is a helper for position-based highlighting. * * @param {Mark[]} markings An array of markings to merge. * * @returns {Mark[]} An array of markings where consecutive and overlapping markings are merged. */ const mergeConsecutiveAndOverlappingMarkings = markings => { const newMarkings = []; // Sort markings by start offset. This is probably redundant, but for extra safety. markings.sort(function (a, b) { return a.getPositionStart() - b.getPositionStart(); }); markings.forEach(marking => { if (newMarkings.length === 0) { newMarkings.push(marking); return; } const lastMarking = newMarkings[newMarkings.length - 1]; // Adding 1 to the position end, with the assumption that the language uses spaces. // When we adapt Japanese to use this helper, this check should also be adapted. if (lastMarking.getPositionEnd() + 1 === marking.getPositionStart() || marking.getPositionStart() <= lastMarking.getPositionEnd()) { // The marking is consecutive to the last marking, so we extend the last marking to include the new marking. lastMarking.setPositionEnd(marking.getPositionEnd()); lastMarking.setBlockPositionEnd(marking.getBlockPositionEnd()); } else { // The marking is not consecutive to the last marking, so we add it to the array by itself. newMarkings.push(marking); } }); return newMarkings; }; /** * Gets the Mark objects of all keyphrase matches in the sentence. * Currently, this function creates Mark objects compatible for both search-based and position-based highlighting. * In a pure position-based highlighting, we don't need to provide 'marked' and 'original' when creating the Mark object. * * @param {Sentence} sentence The sentence to check. * @param {Token[]} matchesInSentence An array containing the keyphrase matches in the sentence. * @param {boolean} areListItemsMerged Whether list items in the parent block are merged. Default is false. * * @returns {Mark[]} The array of Mark objects of the keyphrase matches in the sentence. */ function getMarkingsInSentence(sentence, matchesInSentence, areListItemsMerged = false) { if (matchesInSentence.length === 0) { return []; } // Create the marked sentence that is used for search-based highlighting. const markedSentence = createMarksForSentence(sentence, matchesInSentence); /* * Note that there is a paradigm shift: * With search-based highlighting, there would be one Mark object for the entire sentence. * With position-based highlighting, there is a Mark object for each match. * Hence, in order to be backwards compatible with search-based highlighting, * all Mark objects for a sentence have the same markedSentence. */ const markings = matchesInSentence.map(token => { const attributeId = sentence.parentAttributeId; const isFirstSection = sentence.isParentFirstSectionOfBlock; let parentStartOffset = sentence.parentStartOffset; let parentClientId = sentence.parentClientId; /* The check below is only for Keyphrase distribution assessment where list items are possibly merged. When list items are merged, the parent node of the sentence is an array of nodes. In that case, we need to find the correct parent node that contains the token to get the correct start offset and client id. */ if (areListItemsMerged && !attributeId) { const sentenceParentNodes = sentence.sentenceParentNode; const isArray = Array.isArray(sentenceParentNodes); // Look for the parent node that contains the token. const parentNodeOfToken = isArray && sentenceParentNodes.find(node => { const sourceCodeLocation = node.sourceCodeLocation; return token.sourceCodeRange.startOffset >= sourceCodeLocation.startOffset && token.sourceCodeRange.endOffset <= sourceCodeLocation.endOffset; }); if (parentNodeOfToken) { parentStartOffset = parentNodeOfToken.sourceCodeLocation.startTag.endOffset; parentClientId = parentNodeOfToken.clientId; } } const startOffset = token.sourceCodeRange.startOffset; const endOffset = token.sourceCodeRange.endOffset; return new _Mark.default({ position: { startOffset: startOffset, endOffset: endOffset, // Relative to the start of block positions. startOffsetBlock: startOffset - (parentStartOffset || 0), endOffsetBlock: endOffset - (parentStartOffset || 0), // The client id of the block the match was found in. clientId: parentClientId || "", // The attribute id of the Yoast subblock the match was found in. attributeId: attributeId || "", // Whether the match was found in the first section of the Yoast subblock. isFirstSection: isFirstSection || false }, marked: markedSentence, original: sentence.text }); }); return mergeConsecutiveAndOverlappingMarkings(markings); } var _default = exports.default = getMarkingsInSentence; //# sourceMappingURL=getMarkingsInSentence.js.map