class AdminAssistant
# Copied (and then streamlined) from Rails 2.3.5 in an effort to provide
# consistent functionality all the way back to Rails 2.1.0
class DateTimeRangeEndPointSelector
include ActionView::Helpers::RawOutputHelper
include ActionView::Helpers::TagHelper
DEFAULT_PREFIX = 'date'.freeze unless const_defined?('DEFAULT_PREFIX')
POSITION = {
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
}.freeze unless const_defined?('POSITION')
def initialize(datetime, options = {}, html_options = {})
@options = options.dup
@html_options = html_options.dup
@datetime = datetime
end
def select_datetime
# TODO: Remove tag conditional
# Ideally we could just join select_date and select_date for the tag case
if @options[:tag] && @options[:ignore_date]
select_time
elsif @options[:tag]
order = date_order.dup
order -= [:hour, :minute, :second]
@options[:discard_year] ||= true unless order.include?(:year)
@options[:discard_month] ||= true unless order.include?(:month)
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
@options[:discard_minute] ||= true if @options[:discard_hour]
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
# valid (otherwise it could be 31 and february wouldn't be a valid date)
if @datetime && @options[:discard_day] && !@options[:discard_month]
@datetime = @datetime.change(:day => 1)
end
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
order += [:hour, :minute, :second] unless @options[:discard_hour]
build_selects_from_types(order)
else
"#{select_date}#{@options[:datetime_separator]}#{select_time}"
end
end
def select_date
order = date_order.dup
# TODO: Remove tag conditional
if @options[:tag]
@options[:discard_hour] = true
@options[:discard_minute] = true
@options[:discard_second] = true
@options[:discard_year] ||= true unless order.include?(:year)
@options[:discard_month] ||= true unless order.include?(:month)
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
# If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are
# valid (otherwise it could be 31 and february wouldn't be a valid date)
if @datetime && @options[:discard_day] && !@options[:discard_month]
@datetime = @datetime.change(:day => 1)
end
end
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
build_selects_from_types(order)
end
def select_time
order = []
# TODO: Remove tag conditional
if @options[:tag]
@options[:discard_month] = true
@options[:discard_year] = true
@options[:discard_day] = true
@options[:discard_second] ||= true unless @options[:include_seconds]
order += [:year, :month, :day] unless @options[:ignore_date]
end
order += [:hour, :minute]
order << :second if @options[:include_seconds]
build_selects_from_types(order)
end
def select_second
if @options[:use_hidden] || @options[:discard_second]
build_hidden(:second, sec) if @options[:include_seconds]
else
build_options_and_select(:second, sec)
end
end
def select_minute
if @options[:use_hidden] || @options[:discard_minute]
build_hidden(:minute, min)
else
build_options_and_select(:minute, min, :step => @options[:minute_step])
end
end
def select_hour
if @options[:use_hidden] || @options[:discard_hour]
build_hidden(:hour, hour)
else
build_options_and_select(:hour, hour, :end => 23)
end
end
def select_day
if @options[:use_hidden] || @options[:discard_day]
build_hidden(:day, day)
else
build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false)
end
end
def select_month
if @options[:use_hidden] || @options[:discard_month]
build_hidden(:month, month)
else
month_options = []
1.upto(12) do |month_number|
options = { :value => month_number }
options[:selected] = "selected" if month == month_number
month_options << content_tag(:option, month_name(month_number), options) + "\n"
end
build_select(:month, month_options.join)
end
end
def select_year
if !@datetime || @datetime == 0
val = ''
middle_year = Date.today.year
else
val = middle_year = year
end
if @options[:use_hidden] || @options[:discard_year]
build_hidden(:year, val)
else
options = {}
options[:start] = @options[:start_year] || middle_year - 5
options[:end] = @options[:end_year] || middle_year + 5
options[:step] = options[:start] < options[:end] ? 1 : -1
options[:leading_zeros] = false
build_options_and_select(:year, val, options)
end
end
private
%w( sec min hour day month year ).each do |method|
define_method(method) do
@datetime.kind_of?(Fixnum) ? @datetime : @datetime.send(method) if @datetime
end
end
# Returns translated month names, but also ensures that a custom month
# name array has a leading nil element
def month_names
month_names = @options[:use_month_names] || translated_month_names
month_names.unshift(nil) if month_names.size < 13
month_names
end
# Returns translated month names
# => [nil, "January", "February", "March",
# "April", "May", "June", "July",
# "August", "September", "October",
# "November", "December"]
#
# If :use_short_month option is set
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
def translated_month_names
if @options[:use_short_month]
[nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"]
else
[nil, "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"]
end
end
# Lookup month name for number
# month_name(1) => "January"
#
# If :use_month_numbers option is passed
# month_name(1) => 1
#
# If :add_month_numbers option is passed
# month_name(1) => "1 - January"
def month_name(number)
if @options[:use_month_numbers]
number
elsif @options[:add_month_numbers]
"#{number} - #{month_names[number]}"
else
month_names[number]
end
end
def date_order
@options[:order] || translated_date_order
end
def translated_date_order
[:year, :month, :day]
end
# Build full select tag from date type and options
def build_options_and_select(type, selected, options = {})
build_select(type, build_options(selected, options))
end
# Build select option html from date value and options
# build_options(15, :start => 1, :end => 31)
# => "
#
# ..."
def build_options(selected, options = {})
start = options.delete(:start) || 0
stop = options.delete(:end) || 59
step = options.delete(:step) || 1
leading_zeros = options.delete(:leading_zeros).nil? ? true : false
select_options = []
start.step(stop, step) do |i|
value = leading_zeros ? sprintf("%02d", i) : i
tag_options = { :value => value }
tag_options[:selected] = "selected" if selected == i
select_options << content_tag(:option, value, tag_options)
end
select_options.join("\n") + "\n"
end
# Builds select tag from date type and html select options
# build_select(:month, "...")
# => ""
def build_select(type, select_options_as_html)
select_options = {
:id => input_id_from_type(type),
:name => input_name_from_type(type)
}.merge(@html_options)
select_options.merge!(:disabled => 'disabled') if @options[:disabled]
select_html = "\n"
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
select_html << select_options_as_html.to_s
content_tag(:select, raw(select_html), select_options) + "\n"
end
# Builds a prompt option tag with supplied options or from default options
# prompt_option_tag(:month, :prompt => 'Select month')
# => ""
def prompt_option_tag(type, options)
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
case options
when Hash
prompt = default_options.merge(options)[type.to_sym]
when String
prompt = options
else
prompt = I18n.translate(('datetime.prompts.' + type.to_s).to_sym, :locale => @options[:locale])
end
prompt ? content_tag(:option, prompt, :value => '') : ''
end
# Builds hidden input tag for date part and value
# build_hidden(:year, 2008)
# => ""
def build_hidden(type, value)
tag(:input, {
:type => "hidden",
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
}) + "\n"
end
# Returns the name attribute for the input tag
# => post[written_on(1i)]
def input_name_from_type(type)
prefix = @options[:prefix] || DEFAULT_PREFIX
prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
field_name = @options[:field_name] || type
if @options[:include_position]
field_name += "(#{POSITION[type]}i)"
end
@options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
end
# Returns the id attribute for the input tag
# => "post_written_on_1i"
def input_id_from_type(type)
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
end
# Given an ordering of datetime components, create the selection HTML
# and join them with their appropriate separators.
def build_selects_from_types(order)
select = ''
order.reverse.each do |type|
separator = separator(type) unless type == order.first # don't add on last field
select.insert(0, separator.to_s + send("select_#{type}").to_s)
end
select
end
# Returns the separator for a given datetime component
def separator(type)
case type
when :month, :day
@options[:date_separator]
when :hour
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
when :minute
@options[:time_separator]
when :second
@options[:include_seconds] ? @options[:time_separator] : ""
end
end
end
end