Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Fuzzy Searching Strings #2028 rebased against current master branch to fix merge conflict #2599

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ GEM

PLATFORMS
ruby
x86-mingw32

DEPENDENCIES
compass
53 changes: 45 additions & 8 deletions coffee/lib/abstract-chosen.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class AbstractChosen
@enable_split_word_search = if @options.enable_split_word_search? then @options.enable_split_word_search else true
@group_search = if @options.group_search? then @options.group_search else true
@search_contains = @options.search_contains || false
@search_fuzzy = @options.search_fuzzy || false
@single_backstroke_delete = if @options.single_backstroke_delete? then @options.single_backstroke_delete else true
@max_selected_options = @options.max_selected_options || Infinity
@inherit_select_classes = @options.inherit_select_classes || false
Expand Down Expand Up @@ -157,7 +158,7 @@ class AbstractChosen
searchText = this.get_search_text()
escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
zregex = new RegExp(escapedSearchText, 'i')
regex = this.get_search_regex(escapedSearchText)
regex = this.get_search_regex(searchText)

for option in @results_data

Expand All @@ -183,9 +184,7 @@ class AbstractChosen

if option.search_match
if searchText.length
startpos = option.search_text.search zregex
text = option.search_text.substr(0, startpos + searchText.length) + '</em>' + option.search_text.substr(startpos + searchText.length)
option.search_text = text.substr(0, startpos) + '<em>' + text.substr(startpos)
option.search_text = this.highlight_search_result(regex, zregex, option.search_text, searchText)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this change accidentally indents this line too far.


results_group.group_match = true if results_group?

Expand All @@ -201,10 +200,49 @@ class AbstractChosen
this.update_results_content this.results_option_build()
this.winnow_results_set_highlight()

highlight_search_result: (regex, zregex, value, search) ->
if @search_fuzzy
search_text = []

tokens = search.split(' ').sort (a, b) ->
b.length - a.length

search_string = value.split ' '

for string in search_string
for token in tokens
if string.indexOf('<em>') == -1 && string.indexOf('</em>') == -1
string = string.replace(new RegExp('(' + token.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + ')', 'gi'), '<em>$1</em>')
search_text.push string

result = search_text.join ' '
else
startpos = value.search zregex
text = value.substr(0, startpos + search.length) + '</em>' + value.substr(startpos + search.length)
result = text.substr(0, startpos) + '<em>' + text.substr(startpos)

result

get_tokenized_regex_string: (text) ->
regex_tokens = []
tokens = this.get_tokenized_search_string text

for text in tokens
text = text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
regex_tokens.push "(?=.*" + text + ")"

regex_tokens.join ''

get_tokenized_search_string: (text) ->
text.split ' '

get_search_regex: (escaped_search_string) ->
regex_anchor = if @search_contains then "" else "^"
regex_flag = if @case_sensitive_search then "" else "i"
new RegExp(regex_anchor + escaped_search_string, regex_flag)
if @search_fuzzy
new RegExp(this.get_tokenized_regex_string(escaped_search_string), 'i')
else
regex_anchor = if @search_contains then "" else "^"
regex_flag = if @case_sensitive_search then "" else "i"
new RegExp(regex_anchor + escaped_search_string, regex_flag)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chosen used to pass an escaped string here. If search_fuzzy is true, the unescaped string is being escaped in get_tokenized_regex_string. Else, the unescaped string is being passed directly into the new RegExp – causing an error to be thrown. @StehlikC


search_string_match: (search_string, regex) ->
if regex.test search_string
Expand Down Expand Up @@ -298,4 +336,3 @@ class AbstractChosen
@default_multiple_text: "Select Some Options"
@default_single_text: "Select an Option"
@default_no_result_text: "No results match"

77 changes: 76 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,7 @@ <h2><a name="allow-deselect-on-single-selects" class="anchor" href="#allow-desel

<h2><a name="right-to-left-support" class="anchor" href="#right-to-left-support">Right to Left Support</a></h2>
<div class="side-by-side clearfix">
<p>Chosen supports right to left select boxes too. just add <code class="language-javascript">"chosen-rtl"</code> in addition to <code class="language-javascript">"chosen-select"</code> to your select tags and you are good to go.</p>
<p>Chosen supports right to left select boxes too. Just add <code class="language-javascript">"chosen-rtl"</code> in addition to <code class="language-javascript">"chosen-select"</code> to your select tags and you are good to go.</p>
<pre><code class="language-markup">&lt;select class="chosen-select <strong>chosen-rtl</strong>"&gt;</code></pre>
<div>
<em>Single right to left select</em>
Expand Down Expand Up @@ -1326,6 +1326,80 @@ <h2><a name="right-to-left-support" class="anchor" href="#right-to-left-support"
</div>
</div>

<h2><a name="fuzzy-search-support" class="anchor" href="#fuzzy-search-support">Fuzzy Search Support</a></h2>
<div class="side-by-side clearfix">
<p>Chosen supports fuzzy search in select boxes too. Just add <code class="language-javascript">"search_fuzzy:true"</code> to your options and you are good to go.</p>
<div>
<em>Single Select</em>
<select data-placeholder="Your Favorite Types of Bear" style="width:350px;" class="chosen-select-fuzzy" tabindex="15">
<option value=""></option>
<option>American Black Bear</option>
<option>Asiatic Black Bear</option>
<option>Brown Bear</option>
<option>Giant Panda</option>
<option>Sloth Bear</option>
<option>Sun Bear</option>
<option>Polar Bear</option>
<option>Spectacled Bear</option>
<option>Cocos (Keeling) Islands</option>
</select>
</div>
<div>
<em>Single Select with Groups</em>
<select data-placeholder="Your Favorite Football Team" style="width:350px;" class="chosen-select-fuzzy" tabindex="5">
<option value=""></option>
<optgroup label="NFC EAST">
<option>Dallas Cowboys</option>
<option>New York Giants</option>
<option>Philadelphia Eagles</option>
<option>Washington Redskins</option>
</optgroup>
<optgroup label="NFC NORTH">
<option>Chicago Bears</option>
<option>Detroit Lions</option>
<option>Green Bay Packers</option>
<option>Minnesota Vikings</option>
</optgroup>
<optgroup label="NFC SOUTH">
<option>Atlanta Falcons</option>
<option>Carolina Panthers</option>
<option>New Orleans Saints</option>
<option>Tampa Bay Buccaneers</option>
</optgroup>
<optgroup label="NFC WEST">
<option>Arizona Cardinals</option>
<option>St. Louis Rams</option>
<option>San Francisco 49ers</option>
<option>Seattle Seahawks</option>
</optgroup>
<optgroup label="AFC EAST">
<option>Buffalo Bills</option>
<option>Miami Dolphins</option>
<option>New England Patriots</option>
<option>New York Jets</option>
</optgroup>
<optgroup label="AFC NORTH">
<option>Baltimore Ravens</option>
<option>Cincinnati Bengals</option>
<option>Cleveland Browns</option>
<option>Pittsburgh Steelers</option>
</optgroup>
<optgroup label="AFC SOUTH">
<option>Houston Texans</option>
<option>Indianapolis Colts</option>
<option>Jacksonville Jaguars</option>
<option>Tennessee Titans</option>
</optgroup>
<optgroup label="AFC WEST">
<option>Denver Broncos</option>
<option>Kansas City Chiefs</option>
<option>Oakland Raiders</option>
<option>San Diego Chargers</option>
</optgroup>
</select>
</div>
</div>

<h2><a name="change-update-events" class="anchor" href="#change-update-events">Observing, Updating, and Destroying Chosen</a></h2>
<div class="side-by-side clearfix">
<ul>
Expand Down Expand Up @@ -1464,6 +1538,7 @@ <h2><a name="credits" class="anchor" href="#credits">Credits</a></h2>
<script type="text/javascript">
var config = {
'.chosen-select' : {},
'.chosen-select-fuzzy' : {search_fuzzy:true},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
Expand Down
74 changes: 74 additions & 0 deletions public/index.proto.html
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,79 @@ <h2><a name="right-to-left-support" class="anchor" href="#right-to-left-support"
</div>
</div>

<h2><a name="fuzzy-search-support" class="anchor" href="#fuzzy-search-support">Fuzzy Search Support</a></h2>
<div class="side-by-side clearfix">
<p>Chosen supports fuzzy search in select boxes too. Just add <code class="language-javascript">"search_fuzzy:true"</code> to your options and you are good to go.</p>
<div>
<em>Single Select</em>
<select data-placeholder="Your Favorite Types of Bear" style="width:350px;" class="chosen-select-fuzzy" tabindex="15">
<option value=""></option>
<option>American Black Bear</option>
<option>Asiatic Black Bear</option>
<option>Brown Bear</option>
<option>Giant Panda</option>
<option>Sloth Bear</option>
<option>Sun Bear</option>
<option>Polar Bear</option>
<option>Spectacled Bear</option>
</select>
</div>
<div>
<em>Single Select with Groups</em>
<select data-placeholder="Your Favorite Football Team" style="width:350px;" class="chosen-select-fuzzy" tabindex="5">
<option value=""></option>
<optgroup label="NFC EAST">
<option>Dallas Cowboys</option>
<option>New York Giants</option>
<option>Philadelphia Eagles</option>
<option>Washington Redskins</option>
</optgroup>
<optgroup label="NFC NORTH">
<option>Chicago Bears</option>
<option>Detroit Lions</option>
<option>Green Bay Packers</option>
<option>Minnesota Vikings</option>
</optgroup>
<optgroup label="NFC SOUTH">
<option>Atlanta Falcons</option>
<option>Carolina Panthers</option>
<option>New Orleans Saints</option>
<option>Tampa Bay Buccaneers</option>
</optgroup>
<optgroup label="NFC WEST">
<option>Arizona Cardinals</option>
<option>St. Louis Rams</option>
<option>San Francisco 49ers</option>
<option>Seattle Seahawks</option>
</optgroup>
<optgroup label="AFC EAST">
<option>Buffalo Bills</option>
<option>Miami Dolphins</option>
<option>New England Patriots</option>
<option>New York Jets</option>
</optgroup>
<optgroup label="AFC NORTH">
<option>Baltimore Ravens</option>
<option>Cincinnati Bengals</option>
<option>Cleveland Browns</option>
<option>Pittsburgh Steelers</option>
</optgroup>
<optgroup label="AFC SOUTH">
<option>Houston Texans</option>
<option>Indianapolis Colts</option>
<option>Jacksonville Jaguars</option>
<option>Tennessee Titans</option>
</optgroup>
<optgroup label="AFC WEST">
<option>Denver Broncos</option>
<option>Kansas City Chiefs</option>
<option>Oakland Raiders</option>
<option>San Diego Chargers</option>
</optgroup>
</select>
</div>
</div>

<h2><a name="change-update-events" class="anchor" href="#change-update-events">Observing, Updating, and Destroying Chosen</a></h2>
<div class="side-by-side clearfix">
<ul>
Expand Down Expand Up @@ -1466,6 +1539,7 @@ <h2><a name="credits" class="anchor" href="#credits">Credits</a></h2>
document.observe('dom:loaded', function(evt) {
var config = {
'.chosen-select' : {},
'.chosen-select-fuzzy' : {search_fuzzy:true},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text: "Oops, nothing found!"},
Expand Down
5 changes: 5 additions & 0 deletions public/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ <h3>Example:</h3>
<td>false</td>
<td>By default, Chosen’s search matches starting at the beginning of a word. Setting this option to <code class="language-javascript">true</code> allows matches starting from anywhere within a word. This is especially useful for options that include a lot of special characters or phrases in ()s and []s.</td>
</tr>
<tr>
<td>search_fuzzy</td>
<td>false</td>
<td>By default, Chosen’s search matches starting at the beginning of a word. Setting this option to <code class="language-javascript">true</code> allows matching any part of a word in any part of the string. This is especially useful for options that are long phrases.</td>
</tr>
<tr>
<td>single_backstroke_delete</td>
<td>true</td>
Expand Down