"+b.html.substr(j+h.length),k=k.substr(0,j)+""+k.substr(j)):k=b.html,$(f).innerHTML!==k&&$(f).update(k),this.result_activate($(f)),b.group_array_index!=null&&$(this.results_data[b.group_array_index].dom_id).show()):($(f)===this.result_highlight&&this.result_clear_highlight(),this.result_deactivate($(f)))}}return g<1&&h.length?this.no_results(h):this.winnow_results_set_highlight()},a.prototype.winnow_results_clear=function(){var a,b,c,d,e;this.search_field.clear(),b=this.search_results.select("li"),e=[];for(c=0,d=b.length;c0&&this.results_hide(),this.result_clear_highlight())},a.prototype.keydown_backstroke=function(){return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(this.pending_backstroke=this.search_container.siblings("li.search-choice").last(),this.pending_backstroke.addClassName("search-choice-focus"))},a.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},a.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:return this.backstroke_length=this.search_field.value.length;case 9:return this.mouse_on_container=!1;case 13:return a.preventDefault();case 38:return a.preventDefault(),this.keyup_arrow();case 40:return this.keydown_arrow()}},a.prototype.search_field_scale=function(){var a,b,c,d,e,f,g,h,i;if(this.is_multiple){c=0,g=0,e="position:absolute; left: -1000px; top: -1000px; display:none;",f=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(h=0,i=f.length;hthis.f_width-10&&(g=this.f_width-10),this.search_field.setStyle({width:g+"px"}),a=this.container.getHeight(),this.dropdown.setStyle({top:a+"px"})}},a}(),c.Chosen=a,Prototype.Browser.IE&&/MSIE (\d+\.\d+);/.test(navigator.userAgent)&&(Prototype.BrowserFeatures.Version=new Number(RegExp.$1)),b=function(a){var b,c;return b=new Element.Layout(a),c=b.get("border-left")+b.get("border-right")+b.get("padding-left")+b.get("padding-right")},c.get_side_border_padding=b}.call(this)
\ No newline at end of file
diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee
index 1a76945..38c22a0 100644
--- a/coffee/chosen.jquery.coffee
+++ b/coffee/chosen.jquery.coffee
@@ -6,13 +6,12 @@ root = this
$ = jQuery
$.fn.extend({
- chosen: (data, options) ->
- # Do no harm and return as soon as possible for unsupported browsers, namely IE6 and IE7
+ chosen: (options) ->
return this if $.browser is "msie" and ($.browser.version is "6.0" or $.browser.version is "7.0")
$(this).each((input_field) ->
- new Chosen(this, data, options) unless ($ this).hasClass "chzn-done"
+ new Chosen(this, options) unless ($ this).hasClass "chzn-done"
)
-})
+})
class Chosen extends AbstractChosen
@@ -27,13 +26,13 @@ class Chosen extends AbstractChosen
@container_id = if @form_field.id.length then @form_field.id.replace(/(:|\.)/g, '_') else this.generate_field_id()
@container_id += "_chzn"
- @f_width = @form_field_jq.width()
+ @f_width = @form_field_jq.outerWidth()
@default_text = if @form_field_jq.data 'placeholder' then @form_field_jq.data 'placeholder' else @default_text_default
container_div = ($ "", {
id: @container_id
- class: "chzn-container #{ if @is_rtl then 'chzn-rtl' else '' }"
+ class: "chzn-container#{ if @is_rtl then ' chzn-rtl' else '' }"
style: 'width: ' + (@f_width) + 'px;' #use parens around @f_width so coffeescript doesn't think + ' px' is a function parameter
})
@@ -45,6 +44,7 @@ class Chosen extends AbstractChosen
@form_field_jq.hide().after container_div
@container = ($ '#' + @container_id)
@container.addClass( "chzn-container-" + (if @is_multiple then "multi" else "single") )
+ @container.addClass "chzn-container-single-nosearch" if not @is_multiple and @form_field.options.length <= @disable_search_threshold
@dropdown = @container.find('div.chzn-drop').first()
dd_top = @container.height()
@@ -73,6 +73,7 @@ class Chosen extends AbstractChosen
register_observers: ->
@container.mousedown (evt) => this.container_mousedown(evt)
+ @container.mouseup (evt) => this.container_mouseup(evt)
@container.mouseenter (evt) => this.mouse_enter(evt)
@container.mouseleave (evt) => this.mouse_leave(evt)
@@ -89,24 +90,39 @@ class Chosen extends AbstractChosen
if @is_multiple
@search_choices.click (evt) => this.choices_click(evt)
@search_field.focus (evt) => this.input_focus(evt)
+
+ search_field_disabled: ->
+ @is_disabled = @form_field_jq.attr 'disabled'
+ if(@is_disabled)
+ @container.addClass 'chzn-disabled'
+ @search_field.attr 'disabled', true
+ @selected_item.unbind "focus", @activate_action if !@is_multiple
+ this.close_field()
else
- @selected_item.focus (evt) => this.activate_field(evt)
+ @container.removeClass 'chzn-disabled'
+ @search_field.attr 'disabled', false
+ @selected_item.bind "focus", @activate_action if !@is_multiple
container_mousedown: (evt) ->
- if evt and evt.type is "mousedown"
- evt.stopPropagation()
- if not @pending_destroy_click
- if not @active_field
- @search_field.val "" if @is_multiple
- $(document).click @click_test_action
- this.results_show()
- else if not @is_multiple and evt and ($(evt.target) is @selected_item || $(evt.target).parents("a.chzn-single").length)
- evt.preventDefault()
- this.results_toggle()
+ if !@is_disabled
+ target_closelink = if evt? then ($ evt.target).hasClass "search-choice-close" else false
+ if evt and evt.type is "mousedown"
+ evt.stopPropagation()
+ if not @pending_destroy_click and not target_closelink
+ if not @active_field
+ @search_field.val "" if @is_multiple
+ $(document).click @click_test_action
+ this.results_show()
+ else if not @is_multiple and evt and ($(evt.target) is @selected_item || $(evt.target).parents("a.chzn-single").length)
+ evt.preventDefault()
+ this.results_toggle()
- this.activate_field()
- else
- @pending_destroy_click = false
+ this.activate_field()
+ else
+ @pending_destroy_click = false
+
+ container_mouseup: (evt) ->
+ this.results_reset(evt) if evt.target.nodeName is "ABBR"
blur_test: (evt) ->
this.close_field() if not @active_field and @container.hasClass "chzn-container-active"
@@ -167,7 +183,9 @@ class Chosen extends AbstractChosen
this.choice_build data
else if data.selected and not @is_multiple
@selected_item.find("span").text data.text
+ @selected_item.find("span").first().after "" if @allow_single_deselect
+ this.search_field_disabled()
this.show_search_field_default()
this.search_field_scale()
@@ -274,8 +292,11 @@ class Chosen extends AbstractChosen
choice_destroy_link_click: (evt) ->
evt.preventDefault()
- @pending_destroy_click = true
- this.choice_destroy $(evt.target)
+ if not @is_disabled
+ @pending_destroy_click = true
+ this.choice_destroy $(evt.target)
+ else
+ evt.stopPropagation
choice_destroy: (link) ->
@choices -= 1
@@ -286,6 +307,14 @@ class Chosen extends AbstractChosen
this.result_deselect (link.attr "rel")
link.parents('li').first().remove()
+ results_reset: (evt) ->
+ @form_field.options[0].selected = true
+ @selected_item.find("span").text @default_text
+ this.show_search_field_default()
+ $(evt.target).remove();
+ @form_field_jq.trigger "change"
+ this.results_hide() if @active_field
+
result_select: (evt) ->
if @result_highlight
high = @result_highlight
@@ -293,13 +322,14 @@ class Chosen extends AbstractChosen
this.result_clear_highlight()
- high.addClass "result-selected"
-
if @is_multiple
this.result_deactivate high
else
+ @search_results.find(".result-selected").removeClass "result-selected"
@result_single_selected = high
+ high.addClass "result-selected"
+
position = high_id.substr(high_id.lastIndexOf("_") + 1 )
item = @results_data[position]
item.selected = true
@@ -310,6 +340,7 @@ class Chosen extends AbstractChosen
this.choice_build item
else
@selected_item.find("span").first().text item.text
+ @selected_item.find("span").first().after "" if @allow_single_deselect
this.results_hide() unless evt.metaKey and @is_multiple
@@ -319,10 +350,10 @@ class Chosen extends AbstractChosen
this.search_field_scale()
result_activate: (el) ->
- el.addClass("active-result").show()
+ el.addClass("active-result")
result_deactivate: (el) ->
- el.removeClass("active-result").hide()
+ el.removeClass("active-result")
result_deselect: (pos) ->
result_data = @results_data[pos]
@@ -404,13 +435,13 @@ class Chosen extends AbstractChosen
winnow_results_set_highlight: ->
if not @result_highlight
- selected_results = if not @is_multiple then @search_results.find(".result-selected") else []
+ selected_results = if not @is_multiple then @search_results.find(".result-selected.active-result") else []
do_high = if selected_results.length then selected_results.first() else @search_results.find(".active-result").first()
this.result_do_highlight do_high if do_high?
no_results: (terms) ->
- no_results_html = $('
No results match ""
')
+ no_results_html = $('
' + @results_none_found + ' ""
')
no_results_html.find("span").first().html(terms)
@search_results.append no_results_html
@@ -474,8 +505,7 @@ class Chosen extends AbstractChosen
when 40
this.keydown_arrow()
break
-
-
+
search_field_scale: ->
if @is_multiple
h = 0
diff --git a/coffee/chosen.proto.coffee b/coffee/chosen.proto.coffee
index 0f9193b..682b150 100644
--- a/coffee/chosen.proto.coffee
+++ b/coffee/chosen.proto.coffee
@@ -19,7 +19,7 @@ class Chosen extends AbstractChosen
@single_temp = new Template('#{default}
')
@multi_temp = new Template('
')
@choice_temp = new Template('
#{choice}
')
- @no_results_temp = new Template('
No results match "#{terms}"
')
+ @no_results_temp = new Template('
' + @results_none_found + ' "#{terms}"
')
set_up_html: ->
@container_id = @form_field.identify().replace(/(:|\.)/g, '_') + "_chzn"
@@ -28,7 +28,7 @@ class Chosen extends AbstractChosen
container_props =
'id': @container_id
- 'class': "chzn-container #{ if @is_rtl then 'chzn-rtl' else '' }"
+ 'class': "chzn-container#{ if @is_rtl then ' chzn-rtl' else '' }"
'style': 'width: ' + (@f_width) + 'px' #use parens around @f_width so coffeescript doesn't think + ' px' is a function parameter
@default_text = if @form_field.readAttribute 'data-placeholder' then @form_field.readAttribute 'data-placeholder' else @default_text_default
@@ -38,6 +38,7 @@ class Chosen extends AbstractChosen
@form_field.hide().insert({ after: base_template })
@container = $(@container_id)
@container.addClassName( "chzn-container-" + (if @is_multiple then "multi" else "single") )
+ @container.addClassName "chzn-container-single-nosearch" if not @is_multiple and @form_field.options.length <= @disable_search_threshold
@dropdown = @container.down('div.chzn-drop')
dd_top = @container.getHeight()
@@ -65,6 +66,7 @@ class Chosen extends AbstractChosen
register_observers: ->
@container.observe "mousedown", (evt) => this.container_mousedown(evt)
+ @container.observe "mouseup", (evt) => this.container_mouseup(evt)
@container.observe "mouseenter", (evt) => this.mouse_enter(evt)
@container.observe "mouseleave", (evt) => this.mouse_leave(evt)
@@ -81,23 +83,38 @@ class Chosen extends AbstractChosen
if @is_multiple
@search_choices.observe "click", (evt) => this.choices_click(evt)
@search_field.observe "focus", (evt) => this.input_focus(evt)
+
+ search_field_disabled: ->
+ @is_disabled = @form_field.disabled
+ if(@is_disabled)
+ @container.addClassName 'chzn-disabled'
+ @search_field.disabled = true
+ @selected_item.stopObserving "focus", @activate_action if !@is_multiple
+ this.close_field()
else
- @selected_item.observe "focus", (evt) => this.activate_field(evt)
+ @container.removeClassName 'chzn-disabled'
+ @search_field.disabled = false
+ @selected_item.observe "focus", @activate_action if !@is_multiple
container_mousedown: (evt) ->
- if evt and evt.type is "mousedown"
- evt.stop()
- if not @pending_destroy_click
- if not @active_field
- @search_field.clear() if @is_multiple
- document.observe "click", @click_test_action
- this.results_show()
- else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chzn-single"))
- this.results_toggle()
+ if !@is_disabled
+ target_closelink = if evt? then evt.target.hasClassName "search-choice-close" else false
+ if evt and evt.type is "mousedown"
+ evt.stop()
+ if not @pending_destroy_click and not target_closelink
+ if not @active_field
+ @search_field.clear() if @is_multiple
+ document.observe "click", @click_test_action
+ this.results_show()
+ else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chzn-single"))
+ this.results_toggle()
- this.activate_field()
- else
- @pending_destroy_click = false
+ this.activate_field()
+ else
+ @pending_destroy_click = false
+
+ container_mouseup: (evt) ->
+ this.results_reset(evt) if evt.target.nodeName is "ABBR"
blur_test: (evt) ->
this.close_field() if not @active_field and @container.hasClassName("chzn-container-active")
@@ -158,7 +175,9 @@ class Chosen extends AbstractChosen
this.choice_build data
else if data.selected and not @is_multiple
@selected_item.down("span").update( data.html )
+ @selected_item.down("span").insert { after: "" } if @allow_single_deselect
+ this.search_field_disabled()
this.show_search_field_default()
this.search_field_scale()
@@ -268,8 +287,9 @@ class Chosen extends AbstractChosen
choice_destroy_link_click: (evt) ->
evt.preventDefault()
- @pending_destroy_click = true
- this.choice_destroy evt.target
+ if not @is_disabled
+ @pending_destroy_click = true
+ this.choice_destroy evt.target
choice_destroy: (link) ->
@choices -= 1
@@ -280,17 +300,26 @@ class Chosen extends AbstractChosen
this.result_deselect link.readAttribute("rel")
link.up('li').remove()
+ results_reset: (evt) ->
+ @form_field.options[0].selected = true
+ @selected_item.down("span").update(@default_text)
+ this.show_search_field_default()
+ evt.target.remove()
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
+ this.results_hide() if @active_field
+
result_select: (evt) ->
if @result_highlight
high = @result_highlight
this.result_clear_highlight()
- high.addClassName("result-selected")
-
if @is_multiple
this.result_deactivate high
else
+ @search_results.descendants(".result-selected").invoke "removeClassName", "result-selected"
@result_single_selected = high
+
+ high.addClassName("result-selected")
position = high.id.substr(high.id.lastIndexOf("_") + 1 )
item = @results_data[position]
@@ -302,6 +331,7 @@ class Chosen extends AbstractChosen
this.choice_build item
else
@selected_item.down("span").update(item.html)
+ @selected_item.down("span").insert { after: "" } if @allow_single_deselect
this.results_hide() unless evt.metaKey and @is_multiple
@@ -311,10 +341,10 @@ class Chosen extends AbstractChosen
this.search_field_scale()
result_activate: (el) ->
- el.addClassName("active-result").show()
+ el.addClassName("active-result")
result_deactivate: (el) ->
- el.removeClassName("active-result").hide()
+ el.removeClassName("active-result")
result_deselect: (pos) ->
result_data = @results_data[pos]
@@ -396,7 +426,7 @@ class Chosen extends AbstractChosen
if not @result_highlight
if not @is_multiple
- do_high = @search_results.down(".result-selected")
+ do_high = @search_results.down(".result-selected.active-result")
if not do_high?
do_high = @search_results.down(".active-result")
@@ -500,13 +530,6 @@ if Prototype.Browser.IE
Prototype.BrowserFeatures['Version'] = new Number(RegExp.$1);
-document.observe 'dom:loaded', (evt) ->
- # Do no harm and return as soon as possible for unsupported browsers, namely IE6 and IE7
- return if Prototype.Browser.IE and (Prototype.BrowserFeatures['Version'] is 6 or Prototype.BrowserFeatures['Version'] is 7)
-
- selects = $$(".chzn-select")
- new Chosen select for select in selects
-
get_side_border_padding = (elmt) ->
layout = new Element.Layout(elmt)
side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right")
diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee
index eb66771..e1dcf4c 100644
--- a/coffee/lib/abstract-chosen.coffee
+++ b/coffee/lib/abstract-chosen.coffee
@@ -6,10 +6,9 @@ root = this
class AbstractChosen
- constructor: (elmn) ->
+ constructor: (@form_field, @options={}) ->
this.set_default_values()
- @form_field = elmn
@is_multiple = @form_field.multiple
@default_text_default = if @is_multiple then "Select Some Options" else "Select an Option"
@@ -22,12 +21,16 @@ class AbstractChosen
set_default_values: ->
@click_test_action = (evt) => this.test_active_click(evt)
+ @activate_action = (evt) => this.activate_field(evt)
@active_field = false
@mouse_on_container = false
@results_showing = false
@result_highlighted = null
@result_single_selected = null
+ @allow_single_deselect = if @options.allow_single_deselect? and @form_field.options[0].text == "" then @options.allow_single_deselect else false
+ @disable_search_threshold = @options.disable_search_threshold || 0
@choices = 0
+ @results_none_found = @options.no_results_text or "No results match"
mouse_enter: -> @mouse_on_container = true
mouse_leave: -> @mouse_on_container = false
@@ -43,12 +46,15 @@ class AbstractChosen
result_add_option: (option) ->
if not option.disabled
option.dom_id = @container_id + "_o_" + option.array_index
-
+
classes = if option.selected and @is_multiple then [] else ["active-result"]
classes.push "result-selected" if option.selected
classes.push "group-option" if option.group_array_index?
-
- '
' + option.html + '
'
+ classes.push option.classes if option.classes != ""
+
+ style = if option.style.cssText != "" then " style=\"#{option.style}\"" else ""
+
+ '
' + option.html + '
'
else
""
diff --git a/coffee/lib/select-parser.coffee b/coffee/lib/select-parser.coffee
index 69f9bd7..82b6ce1 100644
--- a/coffee/lib/select-parser.coffee
+++ b/coffee/lib/select-parser.coffee
@@ -34,6 +34,8 @@ class SelectParser
selected: option.selected
disabled: if group_disabled is true then group_disabled else option.disabled
group_array_index: group_position
+ classes: option.className
+ style: option.style.cssText
else
@parsed.push
array_index: @parsed.length
diff --git a/example.jquery.html b/example.jquery.html
index 613691e..846d6c4 100644
--- a/example.jquery.html
+++ b/example.jquery.html
@@ -1188,7 +1188,7 @@
- Multiple Select with Groups
+ Multiple Select
+
+
No Results Text Support
+
+
Setting the "No results" search text is as easy as passing an option when you create Chosen:
+
+ $(".chzn-select").chosen({no_results_text: "No results matched"});
+
+
+
+
Allow Deselect on Single Selects
+
+
When a single select box isn't a required field, you can set allow_single_deselect: true and Chosen will add a UI element for option deselection. This will only work if the first option has blank text.