UNPKG

@atlaskit/renderer

Version:
66 lines (62 loc) 2.85 kB
// getIndexMatch finds the position of a given string within a given document, in accordance to the Confluence Annotation backend // The document is serialised into one large string, excluding any nodes that can not have annotations (eg: emojis, media). // Finds where the given query string is relative to the serialised partial document export function getIndexMatch(doc, schema, selectedText, startIndex) { let textContent = ''; let matchIndex = 0; let numMatches = 0; let blockNodePos; doc.descendants((node, pos) => { const nodeType = node.type; const { media } = schema.nodes; // Mirrors Confluence backend and doesn't construct textContent if it doesn't allow annotations // Don't skip media node so that block node can be defined its startIndex in within [nodeStart, nodeEnd] if ((node.isText || !nodeType.allowsMarkType(schema.marks.annotation)) && nodeType !== media) { // Note: `return true` as a parent disallowing annotations does not mean a child disallows annotations. // Eg: panel (invalid) > p (valid) return true; } const nodeStart = pos; const nodeEnd = nodeStart + node.nodeSize; if (startIndex >= nodeStart && startIndex <= nodeEnd) { // if it's a node block, set position to pos to indicate to the backend // that it's an annotation on a block node if (nodeType === media) { blockNodePos = pos; } // If the start of the annotation selection is within the current node, we scan the document for previous occurrences // Find the index by counting all previous instances of the selectedText in the partial textContent // Need to scan from start, up to `startIndex` (which includes partial of the current node) textContent += doc.textBetween(nodeStart, startIndex - 1); matchIndex = countMatches(textContent, selectedText); // Complete appending of the node textContent += doc.textBetween(startIndex, nodeEnd); } else { textContent += node.textContent; } return true; }); // Count total number of matches in final text numMatches = countMatches(textContent, selectedText); return { numMatches, matchIndex, textContent, blockNodePos }; } // countMatches finds the total number of occurrences of `query` within a given `searchString` export function countMatches(searchString, query) { if (searchString === '' || query === '') { return 0; } // Escape characters that would trigger as syntax in a regex query before converting to the query // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp const reg = new RegExp(query.replace(/(?=[.\\+*?[^\]$(){}\|])/g, '\\'), 'g'); return (searchString.match(reg) || []).length; }