upfront-editable
Version:
Friendly contenteditable API
95 lines (77 loc) • 2.58 kB
JavaScript
import $ from 'jquery'
export default class WordHighlighting {
// @param markerNode {DOM Node}
// @param matchMode {String} either 'word' or 'text'
constructor (markerNode, matchMode = 'word') {
this.marker = markerNode
this.matchMode = matchMode
}
findMatches (text, highlights) {
if (!text) return
if (highlights && highlights.length > 0) {
return this.searchMatches(text, highlights)
}
}
searchMatches (text, highlights) {
const createRegex = this.matchMode === 'word'
? createWordRegex
: createHighlightRegex
const regex = createRegex(highlights)
const matches = []
let match
while ((match = regex.exec(text))) {
matches.push(match)
}
return matches.map((entry) => this.prepareMatch(entry))
}
prepareMatch (match) {
let startIndex
let matchedText
if (this.matchMode === 'word') {
startIndex = match.index + match[1].length
matchedText = match[2]
} else {
startIndex = match.index
matchedText = match[0]
}
return {
startIndex,
endIndex: startIndex + matchedText.length,
match: matchedText,
marker: this.marker
}
}
}
function createHighlightRegex (words) {
const escapedWords = $.map(words, (word) => escapeRegEx(word))
const regex = `(${escapedWords.join('|')})`
return new RegExp(regex, 'g')
}
// Regex to find whole words within a string
//
// Unicode character blocks for letters.
// See: http://jrgraphix.net/research/unicode_blocks.php
//
// \\u0041-\\u005A A-Z (Basic Latin)
// \\u0061-\\u007A a-z (Basic Latin)
// \\u0030-\\u0039 0-9 (Basic Latin)
// \\u00AA ª (Latin-1 Supplement)
// \\u00B5 µ (Latin-1 Supplement)
// \\u00BA º (Latin-1 Supplement)
// \\u00C0-\\u00D6 À-Ö (Latin-1 Supplement)
// \\u00D8-\\u00F6 Ø-ö (Latin-1 Supplement)
// \\u00F8-\\u00FF ø-ÿ (Latin-1 Supplement)
// \\u0100-\\u017F Ā-ſ (Latin Extended-A)
// \\u0180-\\u024F ƀ-ɏ (Latin Extended-B)
const letterChars = '\\u0041-\\u005A\\u0061-\\u007A\\u0030-\\u0039\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\u0100-\\u017F\\u0180-\\u024F'
function createWordRegex (words) {
const escapedWords = $.map(words, (word) => escapeRegEx(word))
// (notLetter|^)(words)(?=notLetter|$)
const regex = `([^${letterChars}]|^)` +
`(${escapedWords.join('|')})` +
`(?=[^${letterChars}]|$)`
return new RegExp(regex, 'g')
}
function escapeRegEx (s) {
return String(s).replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1')
}