From dd7979ec08a94be6470aa355c6da12db5b2ec20b Mon Sep 17 00:00:00 2001 From: Miguel Andrade Date: Fri, 5 Oct 2018 16:25:30 +0100 Subject: [PATCH] correctly escape autocomplete-highlight text. closes #985 --- .../paper-autocomplete-highlight.js | 76 ++++++++++++++----- .../paper-autocomplete-highlight.hbs | 8 +- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/addon/components/paper-autocomplete-highlight.js b/addon/components/paper-autocomplete-highlight.js index 100122fad..d567ccf08 100644 --- a/addon/components/paper-autocomplete-highlight.js +++ b/addon/components/paper-autocomplete-highlight.js @@ -4,7 +4,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; -import { htmlSafe } from '@ember/string'; import layout from '../templates/components/paper-autocomplete-highlight'; /** @@ -16,32 +15,67 @@ export default Component.extend({ tagName: 'span', flags: '', - highlight: computed('searchText', 'label', 'flags', function() { - let text = `${this.get('label')}`; - let flags = this.get('flags'); - let regex = this.getRegExp(this.get('searchText'), flags); + tokens: computed('regex', 'label', function() { + let string = `${this.get('label')}`; + let regex = this.get('regex'); - let html = text.replace(regex, '$&'); - return htmlSafe(html); - }), + let tokens = []; + let lastIndex = 0; + + // Use replace here, because it supports global and single regular expressions at same time. + string.replace(regex, (match, index) => { + let prev = string.slice(lastIndex, index); + if (prev) { + tokens.push({ + text: prev, + isMatch: false + }); + } + + tokens.push({ + text: match, + isMatch: true + }); - sanitize(term) { - if (!term) { - return term; + lastIndex = index + match.length; + }); + + // Append the missing text as a token. + let last = string.slice(lastIndex); + if (last) { + tokens.push({ + text: last, + isMatch: false + }); } - return term.replace(/[\\^$*+?.()|{}[\]]/g, '\\$&'); - }, - getRegExp(text, flags) { - let str = ''; - if (flags.indexOf('^') >= 1) { - str += '^'; + return tokens; + }), + + regex: computed('searchText', 'flags', function() { + let flags = this.get('flags'); + let text = this.get('searchText'); + return this.getRegExp(text, flags); + }), + + getRegExp(term, flags) { + let startFlag = ''; + let endFlag = ''; + let regexTerm = this.sanitizeRegex(term); + + if (flags.indexOf('^') >= 0) { + startFlag = '^'; } - str += text; - if (flags.indexOf('$') >= 1) { - str += '$'; + + if (flags.indexOf('$') >= 0) { + endFlag = '$'; } - return new RegExp(this.sanitize(str), flags.replace(/[$^]/g, '')); + + return new RegExp(startFlag + regexTerm + endFlag, flags.replace(/[$^]/g, '')); + }, + + sanitizeRegex(term) { + return term && term.toString().replace(/[\\^$*+?.()|{}[\]]/g, '\\$&'); } }); diff --git a/addon/templates/components/paper-autocomplete-highlight.hbs b/addon/templates/components/paper-autocomplete-highlight.hbs index da402e7ac..b74a42fb5 100644 --- a/addon/templates/components/paper-autocomplete-highlight.hbs +++ b/addon/templates/components/paper-autocomplete-highlight.hbs @@ -1 +1,7 @@ -{{highlight}} \ No newline at end of file +{{#each tokens as |token|~}} + {{~#if token.isMatch~}} + {{token.text}} + {{~else~}} + {{token.text}} + {{~/if~}} +{{~/each}} \ No newline at end of file