{"version":3,"file":"ng-text-highlight.mjs","sources":["../../../projects/ng-text-highlight/src/lib/services/text-highlight.service.ts","../../../projects/ng-text-highlight/src/lib/text-highlight.component.ts","../../../projects/ng-text-highlight/src/lib/text-highlight.component.html","../../../projects/ng-text-highlight/src/lib/text-highlight.module.ts","../../../projects/ng-text-highlight/src/public-api.ts","../../../projects/ng-text-highlight/src/ng-text-highlight.ts"],"sourcesContent":["import { TextChunk } from \"../models/text-chunk\";\n\n/**\n * Finds all matching chunks of text based on the provided search words.\n * It processes the text using the provided options (e.g., case sensitivity, escaping).\n * @param autoEscape - Whether to escape special characters in search words.\n * @param caseSensitive - Whether the search should be case-sensitive.\n * @param findChunks - Custom function to find chunks (default is `defaultChunkFinder`).\n * @param sanitize - Function to sanitize the input text and search words.\n * @param searchWords - Array of keywords to search for in the text.\n * @param textToSearch - The main text where the search is performed.\n * @returns Array of `TextChunk` objects representing the highlighted and non-highlighted segments.\n */\nexport const findAllMatches = ({\n  autoEscape,\n  caseSensitive = false,\n  findChunks = defaultChunkFinder,\n  sanitize,\n  searchWords,\n  textToSearch,\n}: {\n  autoEscape?: boolean;\n  caseSensitive?: boolean;\n  findChunks?: typeof defaultChunkFinder;\n  sanitize?: typeof defaultSanitize;\n  searchWords: Array<string>;\n  textToSearch: string;\n}): Array<TextChunk> =>\n  fillChunks({\n    chunksToHighlight: mergeChunks({\n      chunks: findChunks({\n        autoEscape,\n        caseSensitive,\n        sanitize,\n        searchWords,\n        textToSearch,\n      }),\n    }),\n    totalLength: textToSearch ? textToSearch.length : 0,\n  });\n\n/**\n * Merges overlapping or adjacent chunks into a single chunk.\n * Ensures that the chunks are non-overlapping and sorted.\n * @param chunks - Array of `TextChunk` objects to merge.\n * @returns Array of merged `TextChunk` objects.\n */\nconst mergeChunks = ({\n  chunks,\n}: {\n  chunks: Array<TextChunk>;\n}): Array<TextChunk> => {\n  chunks = chunks\n    .sort((first, second) => first.start - second.start)\n    .reduce((mergedChunks: Array<TextChunk>, nextChunk) => {\n      if (mergedChunks.length === 0) {\n        return [nextChunk];\n      } else {\n        const previousChunk: TextChunk | undefined = mergedChunks.pop();\n        if (nextChunk.start <= previousChunk!.end) {\n          const endIndex = Math.max(previousChunk!.end, nextChunk.end);\n          mergedChunks.push({\n            highlight: false,\n            start: previousChunk!.start,\n            end: endIndex,\n          });\n        } else {\n          mergedChunks.push(previousChunk!, nextChunk);\n        }\n        return mergedChunks;\n      }\n    }, []);\n\n  return chunks;\n};\n\n/**\n * Finds chunks of text that match the search words.\n * Uses regular expressions to locate the matches in the text.\n * @param autoEscape - Whether to escape special characters in search words.\n * @param caseSensitive - Whether the search should be case-sensitive.\n * @param sanitize - Function to sanitize the input text and search words.\n * @param searchWords - Array of keywords to search for in the text.\n * @param textToSearch - The main text where the search is performed.\n * @returns Array of `TextChunk` objects representing the matching segments.\n */\nconst defaultChunkFinder = ({\n  autoEscape,\n  caseSensitive,\n  sanitize = defaultSanitize,\n  searchWords,\n  textToSearch,\n}: {\n  autoEscape?: boolean;\n  caseSensitive?: boolean;\n  sanitize?: typeof defaultSanitize;\n  searchWords: Array<string>;\n  textToSearch: string;\n}): Array<TextChunk> => {\n  textToSearch = sanitize(textToSearch);\n  return searchWords\n    .filter((searchWord) => searchWord)\n    .reduce((chunks: Array<TextChunk>, searchWord) => {\n      searchWord = sanitize(searchWord);\n\n      if (autoEscape) {\n        searchWord = escapeRegExp(searchWord);\n      }\n\n      const regex = new RegExp(searchWord, caseSensitive ? \"g\" : \"gi\");\n\n      let match;\n      while ((match = regex.exec(textToSearch))) {\n        let start = match.index;\n        let end = regex.lastIndex;\n        if (end > start) {\n          chunks.push({ highlight: false, start, end });\n        }\n        if (match.index === regex.lastIndex) {\n          regex.lastIndex++;\n        }\n      }\n\n      return chunks;\n    }, []);\n};\n\n/**\n * Fills in the gaps between highlighted chunks to create a complete representation of the text.\n * Ensures that non-highlighted segments are included between highlighted ones.\n * @param chunksToHighlight - Array of `TextChunk` objects representing highlighted segments.\n * @param totalLength - Total length of the text being processed.\n * @returns Array of `TextChunk` objects representing the full text with highlighted and non-highlighted segments.\n */\nconst fillChunks = ({\n  chunksToHighlight,\n  totalLength,\n}: {\n  chunksToHighlight: Array<TextChunk>;\n  totalLength: number;\n}): Array<TextChunk> => {\n  const allChunks: Array<TextChunk> = [];\n  const appendChunk = (start: number, end: number, highlight: boolean) => {\n    if (end - start > 0) {\n      allChunks.push({\n        start,\n        end,\n        highlight,\n      });\n    }\n  };\n\n  if (chunksToHighlight.length === 0) {\n    appendChunk(0, totalLength, false);\n  } else {\n    let lastIndex = 0;\n    chunksToHighlight.forEach((chunk) => {\n      appendChunk(lastIndex, chunk.start, false);\n      appendChunk(chunk.start, chunk.end, true);\n      lastIndex = chunk.end;\n    });\n    appendChunk(lastIndex, totalLength, false);\n  }\n  return allChunks;\n};\n\n/**\n * Default sanitization function that returns the input string as-is.\n * Can be overridden to implement custom sanitization logic.\n * @param string - The input string to sanitize.\n * @returns The sanitized string.\n */\nfunction defaultSanitize(string: string): string {\n  return string;\n}\n\n/**\n * Escapes special characters in a string to make it safe for use in a regular expression.\n * @param string - The input string to escape.\n * @returns The escaped string.\n */\nfunction escapeRegExp(string: string): string {\n  return string.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n}\n","import { NgClass, NgFor, NgIf, NgStyle } from \"@angular/common\";\nimport { Component, Input, OnChanges, SimpleChanges } from \"@angular/core\";\nimport { TextSegment } from \"./models/text-segment\";\nimport { findAllMatches } from \"./services/text-highlight.service\";\nimport { TextChunk } from \"./models/text-chunk\";\n\n/**\n * TextHighlightComponent\n * A standalone Angular component that highlights specified keywords within a block of text.\n */\n@Component({\n  selector: \"ng-text-highlight\",\n  templateUrl: \"./text-highlight.component.html\",\n  styleUrls: [\"./text-highlight.component.scss\"],\n  imports: [NgFor, NgIf, NgClass, NgStyle],\n  standalone: true,\n})\nexport class TextHighlightComponent implements OnChanges {\n  /**\n   * The main text where keywords will be highlighted.\n   */\n  @Input() fullText = \"\";\n\n  /**\n   * List of keywords to highlight in the text.\n   */\n  @Input() keywords!: Array<string>;\n\n  /**\n   * Whether the search should be case-sensitive.\n   */\n  @Input() caseSensitive: boolean = false;\n\n  /**\n   * Custom CSS class for highlighted text.\n   */\n  @Input() highlightClass: string = \"highlight\";\n\n  /**\n   * Inline styles for highlighted text.\n   */\n  @Input() highlightStyle: { [key: string]: string } = {};\n\n  /**\n   * Array of text segments, each representing a portion of the text\n   * with information about whether it is highlighted.\n   */\n  highlightedSegments!: Array<TextSegment>;\n\n  /**\n   * Lifecycle hook that is triggered when any data-bound property changes.\n   * Calls the method to generate highlighted segments.\n   * @param changes - The changes to the input properties.\n   */\n  ngOnChanges(changes: SimpleChanges): void {\n    this.generateHighlightedSegments();\n  }\n\n  /**\n   * Generates the highlighted segments of the text based on the provided keywords.\n   * If no keywords are provided, the entire text is treated as unhighlighted.\n   */\n  generateHighlightedSegments(): void {\n    this.highlightedSegments = [];\n    if (!this.keywords || this.keywords.length === 0) {\n      this.highlightedSegments = [\n        {\n          text: this.fullText,\n          isHighlighted: false,\n        },\n      ];\n      return;\n    }\n\n    // Find all matching chunks in the text\n    const textChunks = findAllMatches({\n      searchWords: this.keywords,\n      textToSearch: this.fullText,\n      autoEscape: true,\n      caseSensitive: this.caseSensitive,\n    });\n\n    // Map chunks to highlighted segments\n    textChunks.forEach((chunk: TextChunk) => {\n      const { end, highlight, start } = chunk;\n      const segmentText = this.fullText.substring(start, end);\n      this.highlightedSegments.push({\n        text: segmentText,\n        isHighlighted: highlight,\n      });\n    });\n  }\n}\n","<span *ngFor=\"let segment of highlightedSegments\">\n  <span\n    *ngIf=\"segment.isHighlighted; else normalText\"\n    [ngClass]=\"highlightClass\"\n    [ngStyle]=\"highlightStyle\"\n  >{{ segment.text }}</span>\n  <ng-template #normalText>{{ segment.text }}</ng-template>\n</span>\n","import { NgModule } from \"@angular/core\";\nimport { TextHighlightComponent } from \"./text-highlight.component\";\n\n@NgModule({\n  declarations: [],\n  imports: [TextHighlightComponent],\n  exports: [TextHighlightComponent],\n})\nexport class TextHighlightModule {}\n","/*\n * Public API Surface of ng-text-highlight\n */\n\nexport * from './lib/text-highlight.module';\nexport * from './lib/text-highlight.component';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}