diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee index 4d0e35d8882..6e1f1ca1296 100644 --- a/coffee/chosen.jquery.coffee +++ b/coffee/chosen.jquery.coffee @@ -200,7 +200,7 @@ class Chosen extends AbstractChosen @search_choices.find("li.search-choice").remove() else this.single_set_selected_text() - if @disable_search or @form_field.options.length <= @disable_search_threshold + if @disable_search or @form_field.options.length <= @disable_search_threshold and not @create_option @search_field[0].readOnly = true @container.addClass "chosen-container-single-nosearch" else @@ -355,6 +355,10 @@ class Chosen extends AbstractChosen if @result_highlight high = @result_highlight + if high.hasClass "create-option" + this.select_create_option(@search_field.val()) + return this.results_hide() + this.result_clear_highlight() if @is_multiple and @max_selected_options <= this.choices_count() @@ -443,9 +447,30 @@ class Chosen extends AbstractChosen no_results: (terms) -> no_results_html = this.get_no_results_html(terms) + @search_results.append no_results_html @form_field_jq.trigger("chosen:no_results", {chosen:this}) + show_create_option: (terms) -> + create_option_html = this.get_create_option_html(terms) + @search_results.append create_option_html + + create_option_clear: -> + @search_results.find(".create-option").remove() + + select_create_option: (terms) -> + if $.isFunction(@create_option) + @create_option.call this, terms + else + this.select_append_option( {value: terms, text: terms} ) + + select_append_option: ( options ) -> + option = this.get_option_html(options) + @form_field_jq.append option + @form_field_jq.trigger "chosen:updated" + @form_field_jq.trigger "change" + @search_field.trigger "focus" + no_results_clear: -> @search_results.find(".no-results").remove() @@ -453,6 +478,8 @@ class Chosen extends AbstractChosen if @results_showing and @result_highlight next_sib = @result_highlight.nextAll("li.active-result").first() this.result_do_highlight next_sib if next_sib + else if @results_showing and @create_option + this.result_do_highlight(@search_results.find('.create-option')) else this.results_show() diff --git a/coffee/chosen.proto.coffee b/coffee/chosen.proto.coffee index 5c7eefac498..777bf2bf765 100644 --- a/coffee/chosen.proto.coffee +++ b/coffee/chosen.proto.coffee @@ -193,7 +193,7 @@ class @Chosen extends AbstractChosen @search_choices.select("li.search-choice").invoke("remove") else this.single_set_selected_text() - if @disable_search or @form_field.options.length <= @disable_search_threshold + if @disable_search or @form_field.options.length <= @disable_search_threshold and not @create_option @search_field.readOnly = true @container.addClassName "chosen-container-single-nosearch" else @@ -347,6 +347,11 @@ class @Chosen extends AbstractChosen result_select: (evt) -> if @result_highlight high = @result_highlight + + if high.hasClassName "create-option" + this.select_create_option(@search_field.value) + return this.results_hide() + this.result_clear_highlight() if @is_multiple and @max_selected_options <= this.choices_count() @@ -439,15 +444,38 @@ class @Chosen extends AbstractChosen @search_results.insert this.get_no_results_html(terms) @form_field.fire("chosen:no_results", {chosen: this}) + show_create_option: (terms) -> + create_option_html = this.get_create_option_html(terms) + @search_results.insert create_option_html + @search_results.down(".create-option").observe "click", (evt) => this.select_create_option(terms) + + create_option_clear: -> + co = null + co.remove() while co = @search_results.down(".create-option") + + select_create_option: ( terms ) -> + if Object.isFunction( @create_option ) + @create_option.call this, terms + else + this.select_append_option( value: terms, text: terms ) + + select_append_option: (options) -> + @form_field.insert this.get_option_html(options) + Event.fire @form_field, "chosen:updated" + if typeof Event.simulate is 'function' + @form_field.simulate("change") + @search_field.simulate("focus") + no_results_clear: -> nr = null nr.remove() while nr = @search_results.down(".no-results") - keydown_arrow: -> if @results_showing and @result_highlight next_sib = @result_highlight.next('.active-result') this.result_do_highlight next_sib if next_sib + else if @results_showing and @create_option + this.result_do_highlight(@search_results.select('.create-option').first()) else this.results_show() diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index 1d252691937..0890d574d21 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -36,6 +36,9 @@ class AbstractChosen @max_shown_results = @options.max_shown_results || Number.POSITIVE_INFINITY @case_sensitive_search = @options.case_sensitive_search || false @hide_results_on_select = if @options.hide_results_on_select? then @options.hide_results_on_select else true + @create_option = @options.create_option || false + @persistent_create_option = @options.persistent_create_option || false + @skip_no_results = @options.skip_no_results || false set_default_text: -> if @form_field.getAttribute("data-placeholder") @@ -48,6 +51,7 @@ class AbstractChosen @default_text = this.escape_html(@default_text) @results_none_found = @form_field.getAttribute("data-no_results_text") || @options.no_results_text || AbstractChosen.default_no_result_text + @create_option_text = @form_field.getAttribute("data-create_option_text") || @options.create_option_text || AbstractChosen.default_create_option_text choice_label: (item) -> if @include_group_label_in_selected and item.group_label? @@ -136,6 +140,9 @@ class AbstractChosen this.outerHTML(group_el) + append_option: (option) -> + this.select_append_option(option) + results_update_field: -> this.set_default_text() this.results_reset_cleanup() if not @is_multiple @@ -163,10 +170,12 @@ class AbstractChosen this.no_results_clear() results = 0 + exact_result = false query = this.get_search_text() escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") regex = this.get_search_regex(escapedQuery) + exactRegex = new RegExp("^#{escapedQuery}$") for option in @results_data @@ -194,6 +203,8 @@ class AbstractChosen results += 1 if option.search_match and not option.group + exact_result = exact_result || exactRegex.test option.html + if option.search_match if query.length startpos = search_match.index @@ -211,11 +222,14 @@ class AbstractChosen if results < 1 and query.length this.update_results_content "" - this.no_results query + this.no_results query unless @create_option and @skip_no_results else this.update_results_content this.results_option_build() this.winnow_results_set_highlight() + if @create_option and (results < 1 or (!exact_result and @persistent_create_option)) and query.length + this.show_create_option( query ) + get_search_regex: (escaped_search_string) -> 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 @@ -362,6 +376,16 @@ class AbstractChosen """ + get_option_html: ({ value, text }) -> + """ + + """ + + get_create_option_html: (terms) -> + """ +
  • #{@create_option_text}: "#{this.escape_html(terms)}"
  • + """ + # class methods and variables ============================================================ @browser_is_supported: -> @@ -379,4 +403,4 @@ class AbstractChosen @default_multiple_text: "Select Some Options" @default_single_text: "Select an Option" @default_no_result_text: "No results match" - + @default_create_option_text: "Add Option" diff --git a/public/create-example.jquery.html b/public/create-example.jquery.html new file mode 100644 index 00000000000..2737d3d59d6 --- /dev/null +++ b/public/create-example.jquery.html @@ -0,0 +1,1473 @@ + + + + + Chosen: A jQuery Plugin by Harvest to Tame Unwieldy Select Boxes + + + + + + + + +
    +
    +
    +
    +

    Chosen (v1.8.3)

    +
    +

    Chosen is a jQuery plugin that makes long, unwieldy select boxes much more user-friendly.

    + +

    + Downloads + Project Source + Contribute +

    + +

    Standard Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    Multiple Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    <optgroup> Support

    +
    +
    + Single Select with Groups + +
    +
    + Multiple Select with Groups + +
    +
    + +

    Selected and Disabled Support

    +
    +

    Chosen automatically highlights selected options and removes disabled options.

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Hide Search on Single Select

    +
    +

    The disable_search_threshold option can be specified to hide the search input on single selects if there are n or fewer options.

    +
    $(".chosen-select").chosen({disable_search_threshold: 10});
    +

    +
    + +
    +
    + +

    Default Text Support

    +
    +

    Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

    +
    <select data-placeholder="Choose a country..." multiple class="chosen-select">
    +

    Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

    +
    + +

    No Results Text Support

    +
    +

    Setting the "No results" search text is as easy as passing an option when you create Chosen:

    +
     $(".chosen-select").chosen({no_results_text: "Oops, nothing found!"}); 
    +

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Limit Selected Options in Multiselect

    +
    +

    You can easily limit how many options the user can select:

    +
    $(".chosen-select").chosen({max_selected_options: 5});
    +

    If you try to select another option with limit reached chosen:maxselected event is triggered:

    +
     $(".chosen-select").bind("chosen:maxselected", function () { ... }); 
    +
    + +

    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.

    +
    + +
    +
    + +

    Right-to-Left Support

    +
    +

    You can set right-to-left text by setting rtl: true

    +
     $(".chosen-select").chosen({rtl: true}); 
    + +
    + Single Right-to-Left Select + +
    +
    + Multiple Right-to-Left Select + +
    +
    + +

    Observing, Updating, and Destroying Chosen

    +
    +
      +
    • +

      Observing Form Field Changes

      +

      When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event on the original form field. That lets you do something like this:

      +
      $("#form_field").chosen().change( … );
      +
    • +
    • +

      Updating Chosen Dynamically

      +

      If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

      +
      $("#form_field").trigger("chosen:updated");
      +
    • +
    • +

      Destroying Chosen

      +

      To destroy Chosen and revert back to the native select:

      +
      $("#form_field").chosen("destroy");
      +
    • +
    +
    + +

    Custom Width Support

    +
    +

    Using a custom width with Chosen is as easy as passing an option when you create Chosen:

    +
     $(".chosen-select").chosen({width: "95%"}); 
    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Labels work, too

    +
    +

    Use labels just like you would a standard select

    +

    +
    + + +
    +
    + + +
    +
    + +

    Setup

    +

    Using Chosen is easy as can be.

    +
      +
    1. Download the plugin and copy the chosen files to your app.
    2. +
    3. Activate the plugin on the select boxes of your choice: $(".chosen-select").chosen()
    4. +
    5. Disco.
    6. +
    + +

    FAQs

    +
      +
    • +

      Do you have all the available options documented somewhere?

      +

      Yes! You can find them on the options page.

      +
    • +
    • +

      Something doesn't work. Can you fix it?

      +

      Yes! Please report all issues using the GitHub issue tracking tool. Please include the plugin version (jQuery or Prototype), browser and OS. The more information provided, the easier it is to fix a problem.

      +
    • +
    • +

      What browsers are supported?

      +

      All modern desktop browsers are supported (Firefox, Chrome, Safari and IE9). Legacy support for IE8 is also enabled. Chosen is disabled on iPhone, iPod Touch, and Android mobile devices (more information).

      +
    • +
    • +

      Didn't there used to be a Prototype version of Chosen?

      +

      There still is!

      +
    • +
    + +

    Credits

    + + + + + +
    +
    + + + + +
    +
    + +
    + + diff --git a/public/create-example.proto.html b/public/create-example.proto.html new file mode 100644 index 00000000000..d36e8375c9c --- /dev/null +++ b/public/create-example.proto.html @@ -0,0 +1,1472 @@ + + + + + Chosen: A Prototype Plugin by Harvest to Tame Unwieldy Select Boxes + + + + + + + + +
    +
    +
    +

    Chosen - Prototype Version (v1.8.3)

    +
    +

    Chosen is a Prototype plugin that makes long, unwieldy select boxes much more user-friendly.

    + +

    + Downloads + Project Source + Contribute +

    + +

    Looking for the jQuery version?

    + +

    Standard Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    Multiple Select

    +
    +
    + Turns This + +
    +
    + Into This + +
    +
    + +

    <optgroup> Support

    +
    +
    + Single Select with Groups + +
    +
    + Multiple Select with Groups + +
    +
    + +

    Selected and Disabled Support

    +
    +

    Chosen automatically highlights selected options and removes disabled options.

    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Hide Search on Single Select

    +
    +

    The disable_search_threshold option can be specified to hide the search input on single selects if there are n or fewer options.

    +
     new Chosen($("chosen_select_field"),{disable_search_threshold: 10}); 
    +

    +
    + +
    +
    + +

    Default Text Support

    +
    +

    Chosen automatically sets the default field text ("Choose a country...") by reading the select element's data-placeholder value. If no data-placeholder value is present, it will default to "Select an Option" or "Select Some Options" depending on whether the select is single or multiple. You can change these elements in the plugin js file as you see fit.

    +
    <select data-placeholder="Choose a country..." multiple class="chosen-select">
    +

    Note: on single selects, the first element is assumed to be selected by the browser. To take advantage of the default text support, you will need to include a blank option as the first element of your select list.

    +
    + +

    No Results Text Support

    +
    +

    Setting the "No results" search text is as easy as passing an option when you create Chosen:

    +
    new Chosen($("chosen_select_field"),{no_results_text: "Oops, nothing found!"}); 
    + +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Limit Selected Options in Multiselect

    +
    +

    You can easily limit how many options the user can select:

    +
    new Chosen($("chosen_select_field"),{max_selected_options: 5}); 
    +

    If you try to select another option with limit reached chosen:maxselected event is triggered:

    +
    $("chosen_select_field").observe("chosen:maxselected", function(evt) { ... }); 
    +
    + +

    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.

    +
    + +
    +
    + +

    Right-to-Left Support

    +
    +

    You can set right-to-left text by setting rtl: true

    +
     $(".chosen-select").chosen({rtl: true}); 
    +
    + Single Right-to-Left Select + +
    +
    + Multiple Right-to-Left Select + +
    +
    + +

    Observing, Updating, and Destroying Chosen

    +
    +
      +
    • +

      Observing Form Field Changes

      +

      When working with form fields, you often want to perform some behavior after a value has been selected or deselected. Whenever a user selects a field in Chosen, it triggers a "change" event on the original form field. That lets you do something like this:

      +
      $("#form_field").chosen().change( … );
      +

      Note: Prototype doesn't offer support for triggering standard browser events. Event.simulate is required to trigger the change event when using the Prototype version.

      +
    • +
    • +

      Updating Chosen Dynamically

      +

      If you need to update the options in your select field and want Chosen to pick up the changes, you'll need to trigger the "chosen:updated" event on the field. Chosen will re-build itself based on the updated content.

      +
      Event.fire($("form_field"), "chosen:updated");
      +
    • +
    • +

      Destroying Chosen

      +

      To destroy Chosen and revert back to the native select, call destroy on the Chosen instance:

      +
      chosen = new Chosen($("form_field"));
      +
      +// ...later
      +chosen.destroy();
      +
    • +
    +
    + +

    Custom Width Support

    +
    +

    Using a custom width with Chosen is as easy as passing an option when you create Chosen:

    +
    new Chosen($("chosen_select_field"),{width: "95%"}); 
    +
    + Single Select + +
    +
    + Multiple Select + +
    +
    + +

    Labels work, too

    +
    +

    Use labels just like you would a standard select

    +

    +
    + + +
    +
    + + +
    +
    + +

    Setup

    +

    Using Chosen is easy as can be.

    +
      +
    1. Download the plugin and copy the chosen files to your app.
    2. +
    3. Activate the plugin by creating a new instance of Chosen: New Chosen(some_form_field,some_options);
    4. +
    5. Disco.
    6. +
    + +

    FAQs

    + + +

    Credits

    + + + + + +
    +
    + + + + +
    + +
    + + diff --git a/public/docsupport/init.js b/public/docsupport/init.js index 750b408522b..834c6a6e721 100644 --- a/public/docsupport/init.js +++ b/public/docsupport/init.js @@ -1,5 +1,5 @@ var config = { - '.chosen-select' : {}, + '.chosen-select' : { create_option: true, skip_no_results: 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!' }, diff --git a/public/docsupport/init.proto.js b/public/docsupport/init.proto.js index 54b61bce731..7aa6c603a57 100644 --- a/public/docsupport/init.proto.js +++ b/public/docsupport/init.proto.js @@ -1,6 +1,6 @@ document.observe('dom:loaded', function(evt) { var config = { - '.chosen-select' : {}, + '.chosen-select' : { create_option: true, skip_no_results: 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!' },