2004-11-23 20:04:44 -05:00
require 'cgi'
require 'erb'
require File . dirname ( __FILE__ ) + '/form_helper'
module ActionView
module Helpers
2005-01-15 13:57:17 -05:00
# Provides a number of methods for turning different kinds of containers into a set of option tags.
2005-02-17 14:38:02 -05:00
# == Options
2005-02-23 07:54:58 -05:00
# The <tt>collection_select</tt>, <tt>country_select</tt>, <tt>select</tt>,
# and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter,
# a hash.
2005-02-17 14:38:02 -05:00
#
2005-04-19 06:59:55 -04:00
# * <tt>:include_blank</tt> - set to true if the first option element of the select element is a blank. Useful if there is not a default value required for the select element. For example,
#
2005-02-17 14:38:02 -05:00
# select("post", "category", Post::CATEGORIES, {:include_blank => true})
#
2005-04-19 06:59:55 -04:00
# could become:
2005-02-17 14:38:02 -05:00
#
# <select name="post[category]">
# <option></option>
# <option>joke</option>
# <option>poem</option>
# </select>
2005-04-19 06:59:55 -04:00
#
2005-07-03 08:23:16 -04:00
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
#
2005-04-19 06:59:55 -04:00
# Another common case is a select tag for an <tt>belongs_to</tt>-associated object. For example,
#
# select("post", "person_id", Person.find_all.collect {|p| [ p.name, p.id ] })
#
# could become:
#
# <select name="post[person_id]">
# <option value="1">David</option>
# <option value="2">Sam</option>
# <option value="3">Tobias</option>
# </select>
2004-11-23 20:04:44 -05:00
module FormOptionsHelper
include ERB :: Util
2004-12-15 06:49:23 -05:00
# Create a select tag and a series of contained option tags for the provided object and method.
2005-02-07 09:15:53 -05:00
# The option currently held by the object will be selected, provided that the object is available.
2005-04-19 06:59:55 -04:00
# See options_for_select for the required format of the choices parameter.
#
# Example with @post.person_id => 1:
# select("post", "person_id", Person.find_all.collect {|p| [ p.name, p.id ] }, { :include_blank => true })
#
# could become:
#
2005-11-23 16:59:20 -05:00
# <select name="post[person_id]">
2005-04-19 06:59:55 -04:00
# <option></option>
# <option value="1" selected="selected">David</option>
# <option value="2">Sam</option>
# <option value="3">Tobias</option>
# </select>
2005-03-06 06:50:41 -05:00
#
2004-12-15 06:49:23 -05:00
# This can be used to provide a default set of options in the standard way: before rendering the create form, a
# new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
# to the database. Instead, a second model object is created when the create request is received.
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
2005-11-23 16:59:20 -05:00
#
# By default, post.person_id is the selected option. Specify :selected => value to use a different selection
# or :selected => nil to leave all options unselected.
2004-11-23 20:04:44 -05:00
def select ( object , method , choices , options = { } , html_options = { } )
2005-11-21 20:32:40 -05:00
InstanceTag . new ( object , method , self , nil , options . delete ( :object ) ) . to_select_tag ( choices , options , html_options )
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2004-12-15 06:49:23 -05:00
# Return select and option tags for the given object and method using options_from_collection_for_select to generate the list of option tags.
2004-11-23 20:04:44 -05:00
def collection_select ( object , method , collection , value_method , text_method , options = { } , html_options = { } )
2005-11-21 20:32:40 -05:00
InstanceTag . new ( object , method , self , nil , options . delete ( :object ) ) . to_collection_select_tag ( collection , value_method , text_method , options , html_options )
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2004-12-15 06:49:23 -05:00
# Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
2004-11-23 20:04:44 -05:00
def country_select ( object , method , priority_countries = nil , options = { } , html_options = { } )
2005-11-21 20:32:40 -05:00
InstanceTag . new ( object , method , self , nil , options . delete ( :object ) ) . to_country_select_tag ( priority_countries , options , html_options )
2004-11-23 20:04:44 -05:00
end
2005-03-06 06:50:41 -05:00
2005-02-23 07:54:58 -05:00
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
# In addition to the <tt>:include_blank</tt> option documented above,
# this method also supports a <tt>:model</tt> option, which defaults
# to TimeZone. This may be used by users to specify a different time
# zone model object. (See #time_zone_options_for_select for more
# information.)
def time_zone_select ( object , method , priority_zones = nil , options = { } , html_options = { } )
2005-11-21 20:32:40 -05:00
InstanceTag . new ( object , method , self , nil , options . delete ( :object ) ) . to_time_zone_select_tag ( priority_zones , options , html_options )
2005-02-23 07:54:58 -05:00
end
2005-03-06 06:50:41 -05:00
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
2004-11-23 20:04:44 -05:00
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +Selected+
2005-03-06 06:50:41 -05:00
# may also be an array of values to be selected when using a multiple select.
2004-11-23 20:04:44 -05:00
#
# Examples (call, result):
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
# <option value="$">Dollar</option>\n<option value="DKK">Kroner</option>
#
2005-02-07 09:15:53 -05:00
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
# <option>VISA</option>\n<option selected="selected">MasterCard</option>
2004-11-23 20:04:44 -05:00
#
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
# <option value="$20">Basic</option>\n<option value="$40" selected="selected">Plus</option>
#
2005-02-07 09:15:53 -05:00
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
# <option selected="selected">VISA</option>\n<option>MasterCard</option>\n<option selected="selected">Discover</option>
2005-02-19 07:22:13 -05:00
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
2004-11-23 20:04:44 -05:00
def options_for_select ( container , selected = nil )
container = container . to_a if Hash === container
2005-03-06 06:50:41 -05:00
options_for_select = container . inject ( [ ] ) do | options , element |
2005-11-10 17:08:20 -05:00
if ! element . is_a? ( String ) and element . respond_to? ( :first ) and element . respond_to? ( :last )
2005-03-14 19:13:14 -05:00
is_selected = ( ( selected . respond_to? ( :include? ) && ! selected . is_a? ( String ) ? selected . include? ( element . last ) : element . last == selected ) )
2004-11-23 20:04:44 -05:00
if is_selected
options << " <option value= \" #{ html_escape ( element . last . to_s ) } \" selected= \" selected \" > #{ html_escape ( element . first . to_s ) } </option> "
else
options << " <option value= \" #{ html_escape ( element . last . to_s ) } \" > #{ html_escape ( element . first . to_s ) } </option> "
end
else
2005-03-14 19:13:14 -05:00
is_selected = ( ( selected . respond_to? ( :include? ) && ! selected . is_a? ( String ) ? selected . include? ( element ) : element == selected ) )
2005-04-17 05:44:28 -04:00
options << ( ( is_selected ) ? " <option value= \" #{ html_escape ( element . to_s ) } \" selected= \" selected \" > #{ html_escape ( element . to_s ) } </option> " : " <option value= \" #{ html_escape ( element . to_s ) } \" > #{ html_escape ( element . to_s ) } </option> " )
2004-11-23 20:04:44 -05:00
end
end
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
options_for_select . join ( " \n " )
end
2005-10-11 23:43:28 -04:00
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
2004-11-23 20:04:44 -05:00
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
# If +selected_value+ is specified, the element returning a match on +value_method+ will get the selected option tag.
#
2005-10-11 23:43:28 -04:00
# Example (call, result). Imagine a loop iterating over each +person+ in <tt>@project.people</tt> to generate an input tag:
2004-11-23 20:04:44 -05:00
# options_from_collection_for_select(@project.people, "id", "name")
# <option value="#{person.id}">#{person.name}</option>
2005-02-19 07:22:13 -05:00
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
2004-11-23 20:04:44 -05:00
def options_from_collection_for_select ( collection , value_method , text_method , selected_value = nil )
options_for_select (
2005-03-06 06:50:41 -05:00
collection . inject ( [ ] ) { | options , object | options << [ object . send ( text_method ) , object . send ( value_method ) ] } ,
2004-11-23 20:04:44 -05:00
selected_value
)
end
2005-10-11 23:43:28 -04:00
# Returns a string of option tags, like options_from_collection_for_select, but surrounds them with <optgroup> tags.
2004-11-23 20:04:44 -05:00
#
# An array of group objects are passed. Each group should return an array of options when calling group_method
2005-11-07 04:36:43 -05:00
# Each group should return its name when calling group_label_method.
2004-11-23 20:04:44 -05:00
#
2005-02-07 09:15:53 -05:00
# html_option_groups_from_collection(@continents, "countries", "continent_name", "country_id", "country_name", @selected_country.id)
2004-11-23 20:04:44 -05:00
#
# Could become:
# <optgroup label="Africa">
# <select>Egypt</select>
# <select>Rwanda</select>
# ...
# </optgroup>
# <optgroup label="Asia">
# <select>China</select>
# <select>India</select>
# <select>Japan</select>
# ...
# </optgroup>
#
# with objects of the following classes:
# class Continent
2005-03-06 06:50:41 -05:00
# def initialize(p_name, p_countries) @continent_name = p_name; @countries = p_countries; end
# def continent_name() @continent_name; end
# def countries() @countries; end
2004-11-23 20:04:44 -05:00
# end
# class Country
2005-03-06 06:50:41 -05:00
# def initialize(id, name) @id = id; @name = name end
# def country_id() @id; end
# def country_name() @name; end
2004-11-23 20:04:44 -05:00
# end
2005-02-19 07:22:13 -05:00
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
2005-03-06 06:50:41 -05:00
def option_groups_from_collection_for_select ( collection , group_method , group_label_method ,
2004-11-23 20:04:44 -05:00
option_key_method , option_value_method , selected_key = nil )
collection . inject ( " " ) do | options_for_select , group |
group_label_string = eval ( " group. #{ group_label_method } " )
options_for_select += " <optgroup label= \" #{ html_escape ( group_label_string ) } \" > "
options_for_select += options_from_collection_for_select ( eval ( " group. #{ group_method } " ) , option_key_method , option_value_method , selected_key )
options_for_select += '</optgroup>'
end
2005-03-06 06:50:41 -05:00
end
# Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
2004-11-23 20:04:44 -05:00
# have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
# that they will be listed above the rest of the (long) list.
2005-02-19 07:22:13 -05:00
#
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
2004-11-23 20:04:44 -05:00
def country_options_for_select ( selected = nil , priority_countries = nil )
country_options = " "
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
if priority_countries
country_options += options_for_select ( priority_countries , selected )
2005-04-17 05:44:28 -04:00
country_options += " <option value= \" \" >-------------</option> \n "
2004-11-23 20:04:44 -05:00
end
if priority_countries && priority_countries . include? ( selected )
country_options += options_for_select ( COUNTRIES - priority_countries , selected )
else
country_options += options_for_select ( COUNTRIES , selected )
end
return country_options
end
2005-02-23 07:54:58 -05:00
# Returns a string of option tags for pretty much any time zone in the
2005-11-16 16:57:30 -05:00
# world. Supply a TimeZone name as +selected+ to have it marked as the
2005-02-23 07:54:58 -05:00
# selected option tag. You can also supply an array of TimeZone objects
# as +priority_zones+, so that they will be listed above the rest of the
# (long) list. (You can use TimeZone.us_zones as a convenience for
# obtaining a list of the US time zones.)
#
2005-02-23 18:55:59 -05:00
# The +selected+ parameter must be either +nil+, or a string that names
# a TimeZone.
#
2005-02-23 07:54:58 -05:00
# By default, +model+ is the TimeZone constant (which can be obtained
# in ActiveRecord as a value object). The only requirement is that the
# +model+ parameter be an object that responds to #all, and returns
# an array of objects that represent time zones.
#
# NOTE: Only the option tags are returned, you have to wrap this call in
# a regular HTML select tag.
def time_zone_options_for_select ( selected = nil , priority_zones = nil , model = TimeZone )
zone_options = " "
2005-02-23 18:55:59 -05:00
zones = model . all
convert_zones = lambda { | list | list . map { | z | [ z . to_s , z . name ] } }
2005-02-23 07:54:58 -05:00
if priority_zones
2005-02-23 18:55:59 -05:00
zone_options += options_for_select ( convert_zones [ priority_zones ] , selected )
2005-04-17 05:44:28 -04:00
zone_options += " <option value= \" \" >-------------</option> \n "
2005-02-23 07:54:58 -05:00
2005-02-23 18:55:59 -05:00
zones = zones . reject { | z | priority_zones . include? ( z ) }
2005-02-23 07:54:58 -05:00
end
2005-02-23 18:55:59 -05:00
zone_options += options_for_select ( convert_zones [ zones ] , selected )
2005-02-23 07:54:58 -05:00
zone_options
end
2004-11-23 20:04:44 -05:00
private
# All the countries included in the country_options output.
2005-02-19 15:15:57 -05:00
COUNTRIES = [ " Afghanistan " , " Albania " , " Algeria " , " American Samoa " , " Andorra " , " Angola " , " Anguilla " ,
2004-11-23 20:04:44 -05:00
" Antarctica " , " Antigua And Barbuda " , " Argentina " , " Armenia " , " Aruba " , " Australia " ,
" Austria " , " Azerbaijan " , " Bahamas " , " Bahrain " , " Bangladesh " , " Barbados " , " Belarus " ,
" Belgium " , " Belize " , " Benin " , " Bermuda " , " Bhutan " , " Bolivia " , " Bosnia and Herzegowina " ,
" Botswana " , " Bouvet Island " , " Brazil " , " British Indian Ocean Territory " ,
" Brunei Darussalam " , " Bulgaria " , " Burkina Faso " , " Burma " , " Burundi " , " Cambodia " ,
" Cameroon " , " Canada " , " Cape Verde " , " Cayman Islands " , " Central African Republic " ,
" Chad " , " Chile " , " China " , " Christmas Island " , " Cocos (Keeling) Islands " , " Colombia " ,
" Comoros " , " Congo " , " Congo, the Democratic Republic of the " , " Cook Islands " ,
2005-06-16 02:36:09 -04:00
" Costa Rica " , " Cote d'Ivoire " , " Croatia " , " Cuba " , " Cyprus " , " Czech Republic " , " Denmark " ,
2004-11-23 20:04:44 -05:00
" Djibouti " , " Dominica " , " Dominican Republic " , " East Timor " , " Ecuador " , " Egypt " ,
" El Salvador " , " England " , " Equatorial Guinea " , " Eritrea " , " Espana " , " Estonia " ,
" Ethiopia " , " Falkland Islands " , " Faroe Islands " , " Fiji " , " Finland " , " France " ,
" French Guiana " , " French Polynesia " , " French Southern Territories " , " Gabon " , " Gambia " ,
" Georgia " , " Germany " , " Ghana " , " Gibraltar " , " Great Britain " , " Greece " , " Greenland " ,
" Grenada " , " Guadeloupe " , " Guam " , " Guatemala " , " Guinea " , " Guinea-Bissau " , " Guyana " ,
" Haiti " , " Heard and Mc Donald Islands " , " Honduras " , " Hong Kong " , " Hungary " , " Iceland " ,
2005-06-16 01:05:43 -04:00
" India " , " Indonesia " , " Ireland " , " Israel " , " Italy " , " Iran " , " Iraq " , " Jamaica " , " Japan " , " Jordan " ,
2004-11-23 20:04:44 -05:00
" Kazakhstan " , " Kenya " , " Kiribati " , " Korea, Republic of " , " Korea (South) " , " Kuwait " ,
" Kyrgyzstan " , " Lao People's Democratic Republic " , " Latvia " , " Lebanon " , " Lesotho " ,
" Liberia " , " Liechtenstein " , " Lithuania " , " Luxembourg " , " Macau " , " Macedonia " ,
" Madagascar " , " Malawi " , " Malaysia " , " Maldives " , " Mali " , " Malta " , " Marshall Islands " ,
" Martinique " , " Mauritania " , " Mauritius " , " Mayotte " , " Mexico " ,
" Micronesia, Federated States of " , " Moldova, Republic of " , " Monaco " , " Mongolia " ,
" Montserrat " , " Morocco " , " Mozambique " , " Myanmar " , " Namibia " , " Nauru " , " Nepal " ,
" Netherlands " , " Netherlands Antilles " , " New Caledonia " , " New Zealand " , " Nicaragua " ,
" Niger " , " Nigeria " , " Niue " , " Norfolk Island " , " Northern Ireland " ,
" Northern Mariana Islands " , " Norway " , " Oman " , " Pakistan " , " Palau " , " Panama " ,
" Papua New Guinea " , " Paraguay " , " Peru " , " Philippines " , " Pitcairn " , " Poland " ,
" Portugal " , " Puerto Rico " , " Qatar " , " Reunion " , " Romania " , " Russia " , " Rwanda " ,
" Saint Kitts and Nevis " , " Saint Lucia " , " Saint Vincent and the Grenadines " ,
" Samoa (Independent) " , " San Marino " , " Sao Tome and Principe " , " Saudi Arabia " ,
2005-05-19 14:30:53 -04:00
" Scotland " , " Senegal " , " Serbia and Montenegro " , " Seychelles " , " Sierra Leone " , " Singapore " ,
" Slovakia " , " Slovenia " , " Solomon Islands " , " Somalia " , " South Africa " ,
2004-11-23 20:04:44 -05:00
" South Georgia and the South Sandwich Islands " , " South Korea " , " Spain " , " Sri Lanka " ,
" St. Helena " , " St. Pierre and Miquelon " , " Suriname " , " Svalbard and Jan Mayen Islands " ,
" Swaziland " , " Sweden " , " Switzerland " , " Taiwan " , " Tajikistan " , " Tanzania " , " Thailand " ,
" Togo " , " Tokelau " , " Tonga " , " Trinidad " , " Trinidad and Tobago " , " Tunisia " , " Turkey " ,
" Turkmenistan " , " Turks and Caicos Islands " , " Tuvalu " , " Uganda " , " Ukraine " ,
" United Arab Emirates " , " United Kingdom " , " United States " ,
" United States Minor Outlying Islands " , " Uruguay " , " Uzbekistan " , " Vanuatu " ,
" Vatican City State (Holy See) " , " Venezuela " , " Viet Nam " , " Virgin Islands (British) " ,
" Virgin Islands (U.S.) " , " Wales " , " Wallis and Futuna Islands " , " Western Sahara " ,
" Yemen " , " Zambia " , " Zimbabwe " ] unless const_defined? ( " COUNTRIES " )
end
class InstanceTag #:nodoc:
include FormOptionsHelper
def to_select_tag ( choices , options , html_options )
2005-03-06 06:50:41 -05:00
html_options = html_options . stringify_keys
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( html_options )
2006-04-25 15:38:52 -04:00
value = value ( object )
2005-11-23 16:59:20 -05:00
selected_value = options . has_key? ( :selected ) ? options [ :selected ] : value
content_tag ( " select " , add_options ( options_for_select ( choices , selected_value ) , options , value ) , html_options )
2004-11-23 20:04:44 -05:00
end
def to_collection_select_tag ( collection , value_method , text_method , options , html_options )
2005-03-06 06:50:41 -05:00
html_options = html_options . stringify_keys
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( html_options )
2006-04-25 15:38:52 -04:00
value = value ( object )
2004-11-23 20:04:44 -05:00
content_tag (
2005-07-03 08:23:16 -04:00
" select " , add_options ( options_from_collection_for_select ( collection , value_method , text_method , value ) , options , value ) , html_options
2004-11-23 20:04:44 -05:00
)
end
2005-03-06 06:50:41 -05:00
2004-11-23 20:04:44 -05:00
def to_country_select_tag ( priority_countries , options , html_options )
2005-03-06 06:50:41 -05:00
html_options = html_options . stringify_keys
2004-11-23 20:04:44 -05:00
add_default_name_and_id ( html_options )
2006-04-25 15:38:52 -04:00
value = value ( object )
2005-07-03 08:23:16 -04:00
content_tag ( " select " , add_options ( country_options_for_select ( value , priority_countries ) , options , value ) , html_options )
2004-11-23 20:04:44 -05:00
end
2005-02-23 07:54:58 -05:00
def to_time_zone_select_tag ( priority_zones , options , html_options )
2005-03-06 06:50:41 -05:00
html_options = html_options . stringify_keys
2005-02-23 07:54:58 -05:00
add_default_name_and_id ( html_options )
2006-04-25 15:38:52 -04:00
value = value ( object )
2005-02-23 07:54:58 -05:00
content_tag ( " select " ,
2005-07-03 08:23:16 -04:00
add_options (
2005-02-23 07:54:58 -05:00
time_zone_options_for_select ( value , priority_zones , options [ :model ] || TimeZone ) ,
2005-07-03 08:23:16 -04:00
options , value
2005-02-23 07:54:58 -05:00
) , html_options
)
end
2004-11-23 20:04:44 -05:00
private
2005-07-03 08:23:16 -04:00
def add_options ( option_tags , options , value = nil )
option_tags = " <option value= \" \" ></option> \n " + option_tags if options [ :include_blank ]
if value . blank? && options [ :prompt ]
( " <option value= \" \" > #{ options [ :prompt ] . kind_of? ( String ) ? options [ :prompt ] : 'Please select' } </option> \n " ) + option_tags
else
option_tags
end
2004-11-23 20:04:44 -05:00
end
end
2005-11-13 06:13:11 -05:00
class FormBuilder
def select ( method , choices , options = { } , html_options = { } )
@template . select ( @object_name , method , choices , options . merge ( :object = > @object ) , html_options )
end
def collection_select ( method , collection , value_method , text_method , options = { } , html_options = { } )
@template . collection_select ( @object_name , method , collection , value_method , text_method , options . merge ( :object = > @object ) , html_options )
end
def country_select ( method , priority_countries = nil , options = { } , html_options = { } )
@template . country_select ( @object_name , method , priority_countries , options . merge ( :object = > @object ) , html_options )
end
def time_zone_select ( method , priority_zones = nil , options = { } , html_options = { } )
@template . time_zone_select ( @object_name , method , priority_zones , options . merge ( :object = > @object ) , html_options )
end
end
2004-11-23 20:04:44 -05:00
end
2005-02-23 18:55:59 -05:00
end