diff --git a/chosen/chosen.jquery.js b/chosen/chosen.jquery.js
new file mode 100644
index 0000000..5d86cb8
--- /dev/null
+++ b/chosen/chosen.jquery.js
@@ -0,0 +1,754 @@
+(function() {
+ /*
+ Chosen for Protoype.js
+ by Patrick Filler for Harvest
+
+ Copyright (c) 2011 Harvest
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */ var $, Chosen, OptionsParser, first_intersect, get_side_border_padding, root;
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+ root = typeof exports !== "undefined" && exports !== null ? exports : this;
+ $ = jQuery;
+ $.fn.extend({
+ chosen: function(data, options) {
+ return $(this).each(function(input_field) {
+ return new Chosen(this, data, options);
+ });
+ }
+ });
+ Chosen = (function() {
+ function Chosen(elmn) {
+ this.set_default_values();
+ this.form_field = elmn;
+ this.is_multiple = this.form_field.multiple;
+ this.default_text_default = this.form_field.multiple ? "Select Some Options" : "Select an Option";
+ this.set_up_html();
+ this.register_observers();
+ }
+ Chosen.prototype.set_default_values = function() {
+ this.click_test_action = __bind(function(evt) {
+ return this.test_active_click(evt);
+ }, this);
+ this.active_field = false;
+ this.mouse_on_container = false;
+ this.results_showing = false;
+ this.result_highlighted = null;
+ this.result_single_selected = null;
+ return this.choices = 0;
+ };
+ Chosen.prototype.set_up_html = function() {
+ var container_div, dd_top, dd_width, sf_width;
+ this.container_id = this.form_field.id + "_chzn";
+ this.f_width = ($(this.form_field)).width();
+ this.default_text = ($(this.form_field)).attr('title') ? ($(this.form_field)).attr('title') : this.default_text_default;
+ container_div = $("
'
+ else
+ ""
+
+ result_add_option: (option) ->
+ if not option.disabled
+ option.dom_id = @form_field.id + "chzn_o_" + option.id
+
+ 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_id >= 0
+
+ '
' + $("").text(option.text).html() + '
'
+ else
+ ""
+
+ results_update_field: ->
+ this.result_clear_highlight()
+ @result_single_selected = null
+ this.results_build()
+
+ result_do_highlight: (el) ->
+ this.result_clear_highlight();
+
+ @result_highlight = el;
+ @result_highlight.addClass "highlighted"
+
+ maxHeight = parseInt @search_results.css("maxHeight"), 10
+ visible_top = @search_results.scrollTop()
+ visible_bottom = maxHeight + visible_top
+
+ high_top = @result_highlight.position().top
+ high_bottom = high_top + @result_highlight.outerHeight()
+
+ #console.log visible_top, visible_bottom, high_top, high_bottom
+
+ if high_bottom >= visible_bottom
+ #console.log "bottom is greater than bottom"
+ @search_results.scrollTop if (high_bottom - maxHeight) > 0 then (high_bottom - maxHeight) else 0
+ else if high_top < visible_top
+ #console.log "top is less than top"
+ @search_results.scrollTop high_top
+
+ result_clear_highlight: ->
+ @result_highlight.removeClass "highlighted" if @result_highlight
+ @result_highlight = null
+
+ results_show: ->
+ if not @is_multiple
+ @selected_item.addClass "chzn-single-with-drop"
+ if @result_single_selected
+ this.result_do_highlight( @result_single_selected )
+
+ dd_top = if @is_multiple then @container.height() else (@container.height() - 1)
+ @dropdown.css {"top": dd_top + "px", "left":0}
+ @results_showing = true
+
+ @search_field.focus()
+ @search_field.val @search_field.val()
+
+ this.winnow_results()
+
+ results_hide: ->
+ @selected_item.removeClass "chzn-single-with-drop" unless @is_multiple
+ this.result_clear_highlight()
+ @dropdown.css {"left":"-9000px"}
+ @results_showing = false
+
+
+ set_tab_index: (el) ->
+ if ($ @form_field).attr "tabIndex"
+ ti = ($ @form_field).attr "tabIndex"
+ ($ @form_field).attr "tabIndex", -1
+
+ if @is_multiple
+ @search_field.attr "tabIndex", ti
+ else
+ @selected_item.attr "tabIndex", ti
+ @search_field.attr "tabIndex", -1
+
+ show_search_field_default: ->
+ if @is_multiple and @choices < 1 and not @active_field
+ @search_field.value = @default_text
+ @search_field.addClass "default"
+ else
+ @search_field.value = ""
+ @search_field.removeClass "default"
+
+ search_results_click: (evt) ->
+ target = if $(evt.target).hasClass "active-result" then $(evt.target) else $(evt.target).parents(".active-result").first()
+ if target
+ # TODO fix
+ @result_highlight = target
+ this.result_select()
+
+ search_results_mouseover: (evt) ->
+ target = if $(evt.target).hasClass "active-result" then $(evt.target) else $(evt.target).parents(".active-result").first()
+ this.result_do_highlight( target ) if target
+
+ search_results_mouseout: (evt) ->
+ this.result_clear_highlight() if $(evt.target).hasClass "active-result" or $(evt.target).parents('.active-result').first()
+
+
+ choices_click: (evt) ->
+ evt.preventDefault()
+ if( @active_field and not($(evt.target).hasClass "search-choice" or $(evt.target).parents('.search-choice').first) and not @results_showing )
+ this.results_show()
+
+ choice_build: (item) ->
+ choice_id = @form_field.id + "_chzn_c_" + item.id
+ @choices += 1
+ @search_container.before '
' + item.text + '
'
+ link = $('#' + choice_id).find("a").first()
+ link.click (evt) => this.choice_destroy_link_click(evt)
+
+ choice_destroy_link_click: (evt) ->
+ evt.preventDefault()
+ @pending_destroy_click = true
+ this.choice_destroy $(evt.target)
+
+ choice_destroy: (link) ->
+ @choices -= 1
+ this.show_search_field_default()
+
+ this.results_hide() if @is_multiple and @choices > 0 and @search_field.value.length < 1
+
+ this.result_deselect (link.attr "rel")
+ link.parents('li').first().remove()
+
+ result_select: ->
+ if @result_highlight
+ high = @result_highlight
+ high_id = high.attr "id"
+
+ this.result_clear_highlight();
+
+ high.addClass "result-selected"
+
+ if @is_multiple
+ this.result_deactivate high
+ else
+ @result_single_selected = high
+
+ position = high_id.substr(high_id.lastIndexOf("_") + 1 )
+ item = @results_data[position]
+ item.selected = true
+
+ @form_field.options[item.select_index].selected = true
+
+ if @is_multiple
+ this.choice_build item
+ else
+ @selected_item.find("span").first().text item.text
+
+ this.results_hide()
+ @search_field.val ""
+
+ # TODO
+ #@form_field.simulate("change") if typeof Event.simulate is 'function'
+ this.search_field_scale()
+
+ result_activate: (el) ->
+ el.addClass("active-result").show()
+
+ result_deactivate: (el) ->
+ el.removeClass("active-result").hide()
+
+ result_deselect: (pos) ->
+ result_data = @results_data[pos]
+ result_data.selected = false
+
+ @form_field.options[result_data.select_index].selected = false
+ result = $(@form_field.id + "chzn_o_" + pos)
+ result.removeClass("result-selected").addClass("active-result").show()
+
+ this.result_clear_highlight()
+ this.winnow_results()
+
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
+ this.search_field_scale()
+
+ results_search: (evt) ->
+ if @results_showing
+ this.winnow_results()
+ else
+ this.results_show()
+
+ winnow_results: ->
+ startTime = new Date()
+ this.no_results_clear()
+
+ results = 0
+
+ searchText = if @search_field.value is @default_text then "" else $.trim @search_field.value
+ regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i')
+ zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i')
+
+ for option in @results_data
+ if not option.disabled
+ if option.group
+ $(option.dom_id).hide()
+ else if not (@is_multiple and option.selected)
+ found = false
+ result_id = @form_field.id + "chzn_o_" + option.id
+
+ if regex.test option.text
+ found = true;
+ results += 1;
+ else if option.text.indexOf(" ") >= 0 or option.text.indexOf("[") == 0
+ #TODO: replace this substitution of /\[\]/ with a list of characters to skip.
+ parts = option.text.replace(/\[|\]/g, "").split(" ")
+ if parts.length
+ for part in parts
+ if regex.test part
+ found = true
+ results += 1
+
+ if found
+ if searchText.length
+ startpos = option.text.search zregex
+ text = option.text.substr(0, startpos + searchText.length) + '' + option.text.substr(startpos + searchText.length)
+ text = text.substr(0, startpos) + '' + text.substr(startpos)
+ else
+ text = option.text
+
+ $(result_id).text text if $(result_id).innerHTML != text
+
+ this.result_activate $(result_id)
+
+ $(@results_data[option.group_id].dom_id).show() if option.group_id?
+ else
+ this.result_clear_highlight() if $(result_id) is @result_highlight
+ this.result_deactivate $(result_id)
+
+ if results < 1 and searchText.length
+ this.no_results(searchText)
+ else
+ this.winnow_results_set_highlight()
+
+ winnow_results_clear: ->
+ @search_field.val ""
+ lis = @search_results.find("li")
+
+ for li in lis
+ li = $(li)
+ if li.hasClass "group-result"
+ li.show()
+ else if not @is_multiple or not li.hasClass "result-selected"
+ this.result_activate li
+
+ winnow_results_set_highlight: ->
+ if not @result_highlight
+ do_high = @search_results.find(".active-result").first()
+ if(do_high)
+ this.result_do_highlight do_high
+
+ no_results: (terms) ->
+ @search_results.insert @no_results_temp.evaluate({"terms":terms.escapeHTML()})
+
+ no_results_clear: ->
+ @search_results.find(".no-results").remove()
+
+ keydown_arrow: ->
+ actives = @search_results.find "li.active-result"
+ if actives.length
+ if not @result_highlight
+ this.result_do_highlight $(actives[0])
+ else if @results_showing
+ sibs = @result_highlight.nextAll()
+ next = first_intersect sibs, actives
+ this.result_do_highlight $(next) if next
+ this.results_show() if not @results_showing
+
+ keyup_arrow: ->
+ if not @results_showing and not @is_multiple
+ this.results_show()
+ else if @result_highlight
+ sibs = @result_highlight.prevAll()
+ actives = @search_results.find "li.active-result"
+ prev = first_intersect sibs, actives
+
+ if prev
+ this.result_do_highlight ($ prev)
+ else
+ this.results_hide() if @choices > 0
+ this.result_clear_highlight()
+
+ keydown_backstroke: ->
+ if @pending_backstroke
+ this.choice_destroy @pending_backstroke.find("a").first()
+ this.clear_backstroke()
+ else
+ @pending_backstroke = @search_container.siblings("li.search-choice").last()
+ @pending_backstroke.addClass "search-choice-focus"
+
+ clear_backstroke: ->
+ @pending_backstroke.removeClass "search-choice-focus" if @pending_backstroke
+ @pending_backstroke = null
+
+ keyup_checker: (evt) ->
+ stroke = evt.which ? evt.keyCode
+ this.search_field_scale()
+
+ switch stroke
+ when 8
+ if @is_multiple and @backstroke_length < 1 and @choices > 0
+ this.keydown_backstroke()
+ else if not @pending_backstroke
+ this.results_search()
+ when 13
+ evt.preventDefault()
+ this.result_select() if this.results_showing
+ when 9, 13, 38, 40, 16
+ # don't do anything on these keys
+ else this.results_search()
+
+
+ keydown_checker: (evt) ->
+ stroke = evt.which ? evt.keyCode
+ this.search_field_scale()
+
+ this.clear_backstroke() if stroke != 8 and this.pending_backstroke
+
+ switch stroke
+ when 8
+ @backstroke_length = this.search_field.value.length
+ when 9
+ @mouse_on_container = false
+ when 13
+ evt.preventDefault()
+ when 38
+ evt.preventDefault()
+ this.keyup_arrow()
+ when 40
+ this.keydown_arrow()
+
+
+ search_field_scale: ->
+ if @is_multiple
+ h = 0
+ w = 0
+
+ style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"
+ styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing']
+
+ for style in styles
+ style_block += style + ":" + @search_field.css(style) + ";"
+
+ div = $('', { 'style' : style_block }).text @search_field.val()
+ $('body').append div
+
+ w = div.width() + 25
+ div.remove()
+
+ if( w > @f_width-10 )
+ w = @f_width - 10
+
+ @search_field.css({'width': w + 'px'})
+
+ dd_top = @container.height()
+ @dropdown.css({"top": dd_top + "px"})
+
+get_side_border_padding = (elmt) ->
+ side_border_padding = elmt.outerWidth() - elmt.width()
+
+root.get_side_border_padding = get_side_border_padding
+
+first_intersect = (a1, a2) ->
+ # TODO for some reason, up arrow doesn't find the first
+ console.log a2
+ for element in a1
+ console.log element
+ console.log $.inArray element, a2
+ return element if $.inArray element, a2
+
+ return null
+
+root.first_intersect = first_intersect
+
+class OptionsParser
+
+ constructor: ->
+ @group_index = 0
+ @sel_index = 0
+ @parsed = []
+
+ add_node: (child) ->
+ if child.nodeName is "OPTGROUP"
+ this.add_group child
+ else
+ this.add_option child
+
+ add_group: (group) ->
+ group_id = @sel_index + @group_index
+ @parsed.push
+ id: group_id
+ group: true
+ label: group.label
+ position: @group_index
+ children: 0
+ disabled: group.disabled
+ this.add_option( option, group_id, group.disabled ) for option in group.childNodes
+ @group_index += 1
+
+ add_option: (option, group_id, group_disabled) ->
+ if option.nodeName is "OPTION" and (@sel_index > 0 or option.text != "")
+ if group_id || group_id is 0
+ @parsed[group_id].children += 1
+ @parsed.push
+ id: @sel_index + @group_index
+ select_index: @sel_index
+ value: option.value
+ text: option.text
+ selected: option.selected
+ disabled: ((group_disabled is true) ? group_disabled : option.disabled)
+ group_id: group_id
+ @sel_index += 1
+
+OptionsParser.select_to_array = (select) ->
+ parser = new OptionsParser()
+ parser.add_node( child ) for child in select.childNodes
+ parser.parsed
+
+root.OptionsParser = OptionsParser
\ No newline at end of file
diff --git a/example.jquery.html b/example.jquery.html
new file mode 100644
index 0000000..a543150
--- /dev/null
+++ b/example.jquery.html
@@ -0,0 +1,1063 @@
+
+
+
+
+
+
+
+
+
Chosen
+
Chosen is a javsacript plug-in for Prototype (jQuery support coming soon) that makes long, unwieldy select boxes much more user-friendly. For more information (including usage, explanation and faqs), check out the online documentation.
+
+
Standard Select
+
+
+ Turns This
+
+
+
+ Into This
+
+
+
+
+
Multiple Select
+
+
+ Turns This
+
+
+
+ Into This
+
+
+
+
+
Setup
+
Using Chosen is easy as can be.
+
+
Download the plug-in and copy the chosen files to your app.