diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee
index 4bc24dc7bda..b42391f0104 100644
--- a/coffee/lib/abstract-chosen.coffee
+++ b/coffee/lib/abstract-chosen.coffee
@@ -217,13 +217,15 @@ class AbstractChosen
this.winnow_results_set_highlight()
get_search_regex: (escaped_search_string) ->
- regex_string = if @search_contains then escaped_search_string else "\\b#{escaped_search_string}\\w*\\b"
+ regex_string = if @search_contains then escaped_search_string else "(^|\\s|\\b)#{escaped_search_string}[^\\s]*"
regex_string = "^#{regex_string}" unless @enable_split_word_search or @search_contains
regex_flag = if @case_sensitive_search then "" else "i"
new RegExp(regex_string, regex_flag)
search_string_match: (search_string, regex) ->
- regex.exec(search_string)
+ match = regex.exec(search_string)
+ match.index += 1 if !@search_contains && match?[1] # make up for lack of lookbehind operator in regex
+ match
choices_count: ->
return @selected_option_count if @selected_option_count?
diff --git a/spec/jquery/searching.spec.coffee b/spec/jquery/searching.spec.coffee
index 8a138d354f1..7368ad8378b 100644
--- a/spec/jquery/searching.spec.coffee
+++ b/spec/jquery/searching.spec.coffee
@@ -91,3 +91,176 @@ describe "Searching", ->
expect(div.find(".no-results").length).toBe(1)
expect(div.find(".no-results").first().html().trim()).toBe("No results match &")
+
+ it "matches in non-ascii languages like Chinese when selecting a single item", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen()
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ expect(div.find(".active-result").length).toBe(12)
+
+ search_field = div.find(".chosen-search-input").first()
+ search_field.val("一")
+ search_field.trigger("keyup")
+
+ expect(div.find(".active-result").length).toBe(1)
+ expect(div.find(".active-result")[0].innerHTML).toBe("一")
+
+ it "matches in non-ascii languages like Chinese when selecting a single item with search_contains", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen({search_contains: true})
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ expect(div.find(".active-result").length).toBe(12)
+
+ search_field = div.find(".chosen-search-input").first()
+ search_field.val("一")
+ search_field.trigger("keyup")
+
+ expect(div.find(".active-result").length).toBe(2)
+ expect(div.find(".active-result")[0].innerHTML).toBe("一")
+ expect(div.find(".active-result")[1].innerHTML).toBe("十一")
+
+ it "matches in non-ascii languages like Chinese when selecting multiple items", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen()
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ expect(div.find(".active-result").length).toBe(12)
+
+ search_field = div.find(".chosen-search-input")
+ search_field.val("一")
+ search_field.trigger("keyup")
+
+ expect(div.find(".active-result").length).toBe(1)
+ expect(div.find(".active-result")[0].innerHTML).toBe("一")
+
+ it "matches in non-ascii languages like Chinese when selecting multiple items with search_contains", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen({search_contains: true})
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ expect(div.find(".active-result").length).toBe(12)
+
+ search_field = div.find(".chosen-search-input")
+ search_field.val("一")
+ search_field.trigger("keyup")
+
+ expect(div.find(".active-result").length).toBe(2)
+ expect(div.find(".active-result")[0].innerHTML).toBe("一")
+ expect(div.find(".active-result")[1].innerHTML).toBe("十一")
+
+ it "highlights results correctly when multiple words are present", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen()
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ expect(div.find(".active-result").length).toBe(1)
+
+ search_field = div.find(".chosen-search-input")
+ search_field.val("h")
+ search_field.trigger("keyup")
+
+ expect(div.find(".active-result").length).toBe(1)
+ expect(div.find(".active-result")[0].innerHTML).toBe("oh hello")
+
+ describe "respects word boundaries when not using search_contains", ->
+ div = $("
").html("""
+
+ """)
+
+ div.find("select").chosen()
+ div.find(".chosen-container").trigger("mousedown") # open the drop
+
+ search_field = div.find(".chosen-search-input")
+
+ div.find("option").each () ->
+ boundary_thing = this.value.slice(1)
+ it "correctly finds words that start after a(n) #{boundary_thing}", ->
+ search_field.val(boundary_thing)
+ search_field.trigger("keyup")
+ expect(div.find(".active-result").length).toBe(1)
+ expect(div.find(".active-result")[0].innerText.slice(1)).toBe(boundary_thing)
diff --git a/spec/proto/searching.spec.coffee b/spec/proto/searching.spec.coffee
index 497ebb0692b..7e20741aac2 100644
--- a/spec/proto/searching.spec.coffee
+++ b/spec/proto/searching.spec.coffee
@@ -96,3 +96,182 @@ describe "Searching", ->
expect(div.select(".no-results").length).toBe(1)
expect(div.down(".no-results").innerHTML.trim()).toBe("No results match &")
+
+ it "matches in non-ascii languages like Chinese when selecting a single item", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"))
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ expect(div.select(".active-result").length).toBe(12)
+
+ search_field = div.down(".chosen-search-input")
+ search_field.value = "一"
+ simulant.fire(search_field, "keyup")
+
+ expect(div.select(".active-result").length).toBe(1)
+ expect(div.select(".active-result")[0].innerHTML).toBe("一")
+
+ it "matches in non-ascii languages like Chinese when selecting a single item with search_contains", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"), {search_contains: true})
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ expect(div.select(".active-result").length).toBe(12)
+
+ search_field = div.down(".chosen-search-input")
+ search_field.value = "一"
+ simulant.fire(search_field, "keyup")
+
+ expect(div.select(".active-result").length).toBe(2)
+ expect(div.select(".active-result")[0].innerHTML).toBe("一")
+ expect(div.select(".active-result")[1].innerHTML).toBe("十一")
+
+ it "matches in non-ascii languages like Chinese when selecting multiple items", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"))
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ expect(div.select(".active-result").length).toBe(12)
+
+ search_field = div.down(".chosen-search-input")
+ search_field.value = "一"
+ simulant.fire(search_field, "keyup")
+
+ expect(div.select(".active-result").length).toBe(1)
+ expect(div.select(".active-result")[0].innerHTML).toBe("一")
+
+ it "matches in non-ascii languages like Chinese when selecting multiple items with search_contains", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"), {search_contains: true})
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ expect(div.select(".active-result").length).toBe(12)
+
+ search_field = div.down(".chosen-search-input")
+ search_field.value = "一"
+ simulant.fire(search_field, "keyup")
+
+ expect(div.select(".active-result").length).toBe(2)
+ expect(div.select(".active-result")[0].innerHTML).toBe("一")
+ expect(div.select(".active-result")[1].innerHTML).toBe("十一")
+
+ it "highlights results correctly when multiple words are present", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"))
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ expect(div.select(".active-result").length).toBe(1)
+
+ search_field = div.down(".chosen-search-input")
+ search_field.value = "h"
+ simulant.fire(search_field, "keyup")
+
+ expect(div.select(".active-result").length).toBe(1)
+ expect(div.select(".active-result")[0].innerHTML).toBe("oh hello")
+
+ describe "respects word boundaries when not using search_contains", ->
+ div = new Element("div")
+ div.update("""
+
+ """)
+
+ new Chosen(div.down("select"))
+ simulant.fire(div.down(".chosen-container"), "mousedown") # open the drop
+
+ search_field = div.down(".chosen-search-input")
+
+ div.select("option").forEach (option) ->
+ boundary_thing = option.value.slice(1)
+ it "correctly finds words that start after a(n) #{boundary_thing}", ->
+ search_field.value = boundary_thing
+ simulant.fire(search_field, "keyup")
+ expect(div.select(".active-result").length).toBe(1)
+ expect(div.select(".active-result")[0].innerText.slice(1)).toBe(boundary_thing)