rd_unobtrusive_date_picker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/MIT-LICENSE +19 -0
  2. data/Manifest +45 -0
  3. data/README.rdoc +165 -0
  4. data/Rakefile +29 -0
  5. data/about.yml +7 -0
  6. data/init.rb +8 -0
  7. data/install.rb +14 -0
  8. data/lib/12_hour_time.rb +102 -0
  9. data/lib/unobtrusive_date_picker.rb +407 -0
  10. data/public/images/backstripes.gif +0 -0
  11. data/public/images/bg_header.jpg +0 -0
  12. data/public/images/bullet1.gif +0 -0
  13. data/public/images/bullet2.gif +0 -0
  14. data/public/images/cal.gif +0 -0
  15. data/public/images/gradient-e5e5e5-ffffff.gif +0 -0
  16. data/public/javascripts/datepicker.js +1445 -0
  17. data/public/javascripts/lang/af.js +40 -0
  18. data/public/javascripts/lang/ar.js +50 -0
  19. data/public/javascripts/lang/de.js +40 -0
  20. data/public/javascripts/lang/du.js +40 -0
  21. data/public/javascripts/lang/en.js +42 -0
  22. data/public/javascripts/lang/es.js +41 -0
  23. data/public/javascripts/lang/fi.js +40 -0
  24. data/public/javascripts/lang/fr.js +44 -0
  25. data/public/javascripts/lang/gr.js +40 -0
  26. data/public/javascripts/lang/he.js +49 -0
  27. data/public/javascripts/lang/it.js +13 -0
  28. data/public/javascripts/lang/nl.js +40 -0
  29. data/public/javascripts/lang/no.js +40 -0
  30. data/public/javascripts/lang/pt.js +50 -0
  31. data/public/javascripts/lang/ro.js +40 -0
  32. data/public/javascripts/lang/ru.js +40 -0
  33. data/public/javascripts/lang/sp.js +40 -0
  34. data/public/javascripts/lang/sv.js +41 -0
  35. data/public/javascripts/lang/ua.js +40 -0
  36. data/public/stylesheets/datepicker.css +263 -0
  37. data/spec/date_picker_tag_spec.rb +122 -0
  38. data/spec/date_picker_text_field_spec.rb +54 -0
  39. data/spec/datepicker_html_class_options_spec.rb +281 -0
  40. data/spec/spec.opts +12 -0
  41. data/spec/spec_helper.rb +52 -0
  42. data/spec/tag_matcher.rb +142 -0
  43. data/spec/unobtrusive_date_picker_spec.rb +129 -0
  44. data/tasks/datepicker_tasks.rake +17 -0
  45. data/uninstall.rb +12 -0
  46. data/unobtrusive_date_picker.gemspec +30 -0
  47. metadata +116 -0
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2007 Brian J. Landau, <brianjlandau@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ MIT-LICENSE
2
+ Manifest
3
+ README.rdoc
4
+ Rakefile
5
+ about.yml
6
+ init.rb
7
+ install.rb
8
+ lib/12_hour_time.rb
9
+ lib/unobtrusive_date_picker.rb
10
+ public/images/backstripes.gif
11
+ public/images/bg_header.jpg
12
+ public/images/bullet1.gif
13
+ public/images/bullet2.gif
14
+ public/images/cal.gif
15
+ public/images/gradient-e5e5e5-ffffff.gif
16
+ public/javascripts/datepicker.js
17
+ public/javascripts/lang/af.js
18
+ public/javascripts/lang/ar.js
19
+ public/javascripts/lang/de.js
20
+ public/javascripts/lang/du.js
21
+ public/javascripts/lang/en.js
22
+ public/javascripts/lang/es.js
23
+ public/javascripts/lang/fi.js
24
+ public/javascripts/lang/fr.js
25
+ public/javascripts/lang/gr.js
26
+ public/javascripts/lang/he.js
27
+ public/javascripts/lang/it.js
28
+ public/javascripts/lang/nl.js
29
+ public/javascripts/lang/no.js
30
+ public/javascripts/lang/pt.js
31
+ public/javascripts/lang/ro.js
32
+ public/javascripts/lang/ru.js
33
+ public/javascripts/lang/sp.js
34
+ public/javascripts/lang/sv.js
35
+ public/javascripts/lang/ua.js
36
+ public/stylesheets/datepicker.css
37
+ spec/date_picker_tag_spec.rb
38
+ spec/date_picker_text_field_spec.rb
39
+ spec/datepicker_html_class_options_spec.rb
40
+ spec/spec.opts
41
+ spec/spec_helper.rb
42
+ spec/tag_matcher.rb
43
+ spec/unobtrusive_date_picker_spec.rb
44
+ tasks/datepicker_tasks.rake
45
+ uninstall.rb
@@ -0,0 +1,165 @@
1
+ = Unobtrusive Date-Picker Widget Plugin
2
+
3
+ This is a helper for creating a date or date-time picker that uses the
4
+ Unobtrusive Date-Picker Widget(sic)
5
+ (http://www.frequency-decoder.com/2006/10/02/unobtrusive-date-picker-widgit-update)
6
+ to add a clickable calendar image that will bring up a calendar picker if
7
+ javascript is available. It replicates as much of the API of the Rails
8
+ `date_select`, and `datetime_select` form helpers.
9
+
10
+ It also uses the 12 Hour Time plugin
11
+ (http://code.google.com/p/rails-twelve-hour-time-plugin/) so that 12 Hour times
12
+ can be processed by Active Record.
13
+
14
+ You may want to consider compressing the javascript files with Dean Edward's
15
+ Packer (http://dean.edwards.name/packer/) or Douglas Crockford's JSMin
16
+ (http://www.crockford.com/javascript/jsmin.html) before deploying your
17
+ application.
18
+
19
+ == Install
20
+
21
+ `script/plugin install git://github.com/brianjlandau/unobtrusive_date_picker.git`
22
+
23
+ == Usage
24
+
25
+ To be able to use the date-picker methods below you need to include the
26
+ javascript library file and CSS stylesheet in the <head> of your layout
27
+ template. You can do this either by manually including the files via a
28
+ `javascript_include_tag` and a `stylesheet_link_tag` (the files are both named
29
+ "datepicker"), OR by using the included `unobtrusive_datepicker_includes` helper
30
+ method, which will do this for you.
31
+
32
+ There are 4 main methods:
33
+ - unobtrusive_date_picker
34
+ - unobtrusive_datetime_picker
35
+ - unobtrusive_date_picker_tags
36
+ - unobtrusive_datetime_picker_tags
37
+ - unobtrusive_date_text_picker
38
+ - unobtrusive_date_text_picker_tag
39
+
40
+ Options (* indicates same functionality as is in Rails Date helpers):
41
+ - *:order => the order the selects should be positioned in
42
+ - *:include_blank => include a blank option at the top of every select
43
+ - *:start_year => year that the year select should start on (defaults to 5
44
+ below either the year of the date value or current year)
45
+ - *:end_year => year that the year select should end on (defaults to 5 above
46
+ either the year of the date value or current year)
47
+ - *:minute_step => how many minutes apart each minute option should be
48
+ - example: (:minute_step => 5) would result in [0, 15, 30, 45] as options
49
+ - *:use_short_month => use short month names (Jan, Feb, etc.) instead of long
50
+ names (January) for option text
51
+ - *:use_month_numbers => use month numbers instead of names for option text
52
+ - *:add_month_numbers => add month numbers to names for option text ("1 - January")
53
+ - :highlight_days => which days of the week should be highlighted in the datepicker (by default Saturday and Sunday)
54
+ - Excepts either a string of number representing days of the week (0 = Monday, 1 = Tuesday ... 6 = Sunday)
55
+ - Or a singular symbol for one day of the week
56
+ - Or an array of symbols representing days of the week, i.e. [:Monday, :Sunday]
57
+ - :range_low => the low range of acceptable dates (not times) for this input
58
+ - managed by the Javascript, thus should be enforced with your own validations
59
+ - Excepts:
60
+ - One of the following symbols: :today, :tomorrow, :yesterday
61
+ - A string representation of a date
62
+ - A Date, DateTime, or Time object
63
+ - :range_high => the high range of acceptable dates (not times) for this input
64
+ - Excepts same options as :range_low; also managed by javascript
65
+ - :disable_days => days of the week that may not be selected
66
+ - Excepts the same format as :highlight_days
67
+ - Also managed by javascript
68
+ - :no_transparency => if set to true it disables the fade in/out
69
+ visual effect of the datepicker
70
+
71
+ The `unobtrusive_date_text_picker` and `unobtrusive_date_text_picker_tag` methods
72
+ don't except the ":order", ":include_blank", ":start_year", ":end_year", ":minute_step", ":use_short_month",
73
+ ":use_month_numbers", and ":add_month_numbers" options.
74
+ It does use these options though:
75
+ - :format => the format the date should be in
76
+ - m-d-y
77
+ - d-m-y
78
+ - y-m-d
79
+ - :divider => the divider used between the dates
80
+ - "slash" or "/"
81
+ - "dash" or "-"
82
+ - "dot" or "."
83
+ - "space" or " "
84
+
85
+
86
+ ==== RJS Method
87
+
88
+ There is an additional RJS method to re initialize the Date Pickers when an AJAX
89
+ response is supplied to the client:
90
+
91
+ - unobtrusive_date_picker_create
92
+ - This method can accept the DOM ID of a year select element to turn into
93
+ a date picker widget or if none is provided it will create all based on
94
+ the appropriate class name.
95
+
96
+
97
+ ==== Rake task
98
+
99
+ There is also a rake task that can be executed by running `rake
100
+ datepicker:update` in your Rails apps root directory. This task will update your
101
+ datepicker javascripts, stylesheets, and images. This is useful if you are using
102
+ an old version of the plugin that had an older version of the Unobtrusive
103
+ Date-Picker Widget Javascript library, or in the future when new version are
104
+ release and I update the plugin with them.
105
+
106
+
107
+
108
+ === Example:
109
+ <% form_for :article, :url => { :action => @form_action, :id => @article } do |f| %>
110
+ <fieldset>
111
+ ...
112
+ <label>Date: <%= f.unobtrusive_datetime_picker :date %></label><br />
113
+ ...
114
+ </fieldset>
115
+ <% end %>
116
+
117
+ === Produces (Current date when output: 12/6/07):
118
+ <form action="/form/create" method="post">
119
+ <fieldset>
120
+
121
+ <label>Date:
122
+ <select id="article_date-dd" name="article[date(3i)]">
123
+ <option value="1">1</option>
124
+ ...
125
+ <option value="31">31</option>
126
+ </select>
127
+ <select id="article_date-mm" name="article[date(2i)]">
128
+ <option value="1">January</option>
129
+ ...
130
+ <option value="12" selected="selected">December</option>
131
+ </select>
132
+ <select id="article_date" name="article[date(1i)]" class="split-date">
133
+ <option value="2002">2002</option>
134
+ ...
135
+ <option value="2012">2012</option>
136
+ </select>
137
+ &nbsp;
138
+ <select id="article_date_4i" name="article[date(4i)][hour]">
139
+ <option value="1">1</option>
140
+ ...
141
+ <option value="12">12</option>
142
+ </select>
143
+ :
144
+ <select id="article_date_5i" name="article[date(5i)][minute]">
145
+ <option value="00">00</option>
146
+ ...
147
+ <option value="59">59</option>
148
+ </select>
149
+ <select id="article_date_6i" name="article[date(6i)][ampm]">
150
+ <option value="AMPM>AMPM</option>
151
+ <option value="AMPM>AMPM</option>
152
+ </select>
153
+ </label><br />
154
+
155
+ </fieldset>
156
+ </form>
157
+
158
+
159
+
160
+ == LICENSE
161
+
162
+ See MIT-LICENSE file for copyright and licensing information for this plugin.
163
+
164
+ Unobtrusive Date-Picker Widget is provided under the Creative Commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/) by frequency-decoder.com
165
+
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'rubygems'
4
+ require 'spec/rake/spectask'
5
+
6
+ desc 'Generate documentation for the unobtrusive_date_picker plugin.'
7
+ Rake::RDocTask.new(:rdoc) do |rdoc|
8
+ rdoc.rdoc_dir = 'rdoc'
9
+ rdoc.title = 'Unobtrusive Date-Picker'
10
+ rdoc.options << '--line-numbers' << '--inline-source'
11
+ rdoc.rdoc_files.add ['lib/**/*.rb', 'README.rdoc']
12
+ rdoc.options << '--main' << 'README.rdoc'
13
+ end
14
+
15
+ desc "Run all specs"
16
+ Spec::Rake::SpecTask.new do |t|
17
+ t.spec_opts = ['--options', 'spec/spec.opts']
18
+ end
19
+
20
+ require 'echoe'
21
+
22
+ Echoe.new('unobtrusive_date_picker', '0.1.0') do |p|
23
+ p.description = "Helper for creating a date or date-time picker that uses the Unobtrusive Date-Picker Widge"
24
+ p.url = "http://github.com/brianjlandau/unobtrusive_date_picker"
25
+ p.author = "Brian landau"
26
+ p.email = ""
27
+ p.ignore_pattern = ["tmp/*", "script/*"]
28
+ p.development_dependencies = []
29
+ end
@@ -0,0 +1,7 @@
1
+ name: Unobtrusive Date-Picker Widget
2
+ author: Brian Landau
3
+ version: 2.0
4
+ description: Helper to create a set of Date/Time selects that use the Unobtrusive Date Picker Widget.
5
+ url: http://github.com/brianjlandau/unobtrusive_date_picker/
6
+ install: git@github.com:brianjlandau/unobtrusive_date_picker.git
7
+ license: MIT
data/init.rb ADDED
@@ -0,0 +1,8 @@
1
+ require '12_hour_time'
2
+ require 'unobtrusive_date_picker'
3
+
4
+ # Include all the necessary functions in to the appropriate point in the Rails framework
5
+ ActionView::Base.send :include, UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
6
+ ActionView::Helpers::DateHelper.send :include, UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
7
+ ActionView::Base.send :include, UnobtrusiveDatePicker::AssetTagHelper
8
+ ActionView::Helpers::AssetTagHelper.send :include, UnobtrusiveDatePicker::AssetTagHelper
@@ -0,0 +1,14 @@
1
+ require 'fileutils'
2
+
3
+ # Install all the needed support files (CSS and JavaScript)
4
+
5
+ js_dir = File.dirname(__FILE__) + '/../../../public/javascripts/'
6
+ datepicker_js = js_dir + 'datepicker.js'
7
+ lang_dir = js_dir + 'lang'
8
+ datepicker_css = File.dirname(__FILE__) + '/../../../public/stylesheets/datepicker.css'
9
+ images_dir = File.dirname(__FILE__) + '/../../../public/images/datepicker'
10
+
11
+ FileUtils.cp File.dirname(__FILE__) + '/public/javascripts/datepicker.js', datepicker_js unless File.exists?(datepicker_js)
12
+ FileUtils.cp_r File.dirname(__FILE__) + '/public/javascripts/lang/', lang_dir unless File.exists?(lang_dir)
13
+ FileUtils.cp File.dirname(__FILE__) + '/public/stylesheets/datepicker.css', datepicker_css unless File.exists?(datepicker_css)
14
+ FileUtils.cp_r File.dirname(__FILE__) + '/public/images/', images_dir unless File.exists?(images_dir)
@@ -0,0 +1,102 @@
1
+ #
2
+ # == Rails Twelve Hour Time Plugin
3
+ #
4
+ # http://code.google.com/p/rails-twelve-hour-time-plugin/
5
+ #
6
+ # ==== Authors
7
+ # * Nick Muerdter (original code)
8
+ # * Maurice Aubrey
9
+ #
10
+ # ==== Used for
11
+ # Allows UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper to use a AM/PM select of it's own,
12
+ # and still be processed correctly by Active Record.
13
+ #
14
+
15
+ # :enddoc:
16
+ if defined? ActiveRecord
17
+ class ActiveRecord::Base # :nodoc: all
18
+ def instantiate_time_object_with_ampm(name, values)
19
+ if values.last < 0
20
+ ampm = values.pop
21
+ if ampm == ActionView::Helpers::DateTimeSelector::AM and values[3] == 12
22
+ values[3] = 0
23
+ elsif ampm == ActionView::Helpers::DateTimeSelector::PM and values[3] != 12
24
+ values[3] += 12
25
+ end
26
+ end
27
+
28
+ instantiate_time_object_without_ampm(name, values)
29
+ end
30
+
31
+ alias_method_chain :instantiate_time_object, :ampm
32
+ end
33
+ end
34
+
35
+ ActionView::Helpers::DateTimeSelector.send(:remove_const, :POSITION)
36
+ ActionView::Helpers::DateTimeSelector.const_set(:POSITION, {
37
+ :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5,
38
+ :second => 6, :ampm => 7
39
+ })
40
+
41
+ # Included manully in UnobtrusiveDatePicker
42
+ # module ActionView::Helpers
43
+ # class DateTimeSelector
44
+ # POSITION = {
45
+ # :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5,
46
+ # :second => 6, :ampm => 7
47
+ # }
48
+ # # XXX would like to do this, but it's frozen
49
+ # # POSITION[:ampm] = 7
50
+ #
51
+ # # We give them negative values so can differentiate between normal
52
+ # # date/time values. The way the multi param stuff works, from what I
53
+ # # can see, results in a variable number of fields (if you tell it to
54
+ # # include seconds, for example). So we expect the AM/PM field, if
55
+ # # present, to be last and have a negative value.
56
+ # AM = -1
57
+ # PM = -2
58
+ #
59
+ # def select_hour_with_ampm
60
+ # unless @options[:twelve_hour]
61
+ # return select_hour_without_ampm
62
+ # end
63
+ #
64
+ # if @options[:use_hidden] || @options[:discard_hour]
65
+ # build_hidden(:hour, hour12)
66
+ # else
67
+ # build_options_and_select(:hour, hour12, :start => 1, :end => 12)
68
+ # end
69
+ # end
70
+ #
71
+ # alias_method_chain :select_hour, :ampm
72
+ #
73
+ # def select_ampm
74
+ # selected = hour < 12 ? AM : PM
75
+ #
76
+ # # XXX i18n?
77
+ # label = { AM => 'AM', PM => 'PM' }
78
+ # ampm_options = []
79
+ # [AM, PM].each do |meridiem|
80
+ # option = { :value => meridiem }
81
+ # option[:selected] = "selected" if selected == meridiem
82
+ # ampm_options << content_tag(:option, label[meridiem], option) + "\n"
83
+ # end
84
+ # build_select(:ampm, ampm_options.join)
85
+ # end
86
+ #
87
+ # private
88
+ #
89
+ # def build_selects_from_types_with_ampm(order)
90
+ # order += [:ampm] if @options[:twelve_hour] and !order.include?(:ampm)
91
+ # build_selects_from_types_without_ampm(order)
92
+ # end
93
+ #
94
+ # alias_method_chain :build_selects_from_types, :ampm
95
+ #
96
+ # def hour12
97
+ # h12 = hour % 12
98
+ # h12 = 12 if h12 == 0
99
+ # return h12
100
+ # end
101
+ # end
102
+ # end
@@ -0,0 +1,407 @@
1
+ require File.join(File.dirname(__FILE__), '12_hour_time')
2
+
3
+ module UnobtrusiveDatePicker
4
+
5
+ DATEPICKER_DEFAULT_NAME_ID_SUFFIXES = { :year => {:id => '', :name => 'year'},
6
+ :month => {:id => 'mm', :name => 'month'},
7
+ :day => {:id => 'dd', :name => 'day'} }
8
+
9
+ DATEPICKER_DAYS_OF_WEEK = { :Monday => '0',
10
+ :Tuesday => '1',
11
+ :Wednesday => '2',
12
+ :Thursday => '3',
13
+ :Friday => '4',
14
+ :Saturday => '5',
15
+ :Sunday => '6'}
16
+
17
+ DATEPICKER_DIVIDERS = { 'slash' => '/',
18
+ 'dash' => '-',
19
+ 'dot' => '.',
20
+ 'space' => ' ' }
21
+
22
+ RANGE_DATE_FORMAT = '%Y-%m-%d'
23
+
24
+ # == Unobtrusive Date-Picker Helper
25
+ #
26
+ # This Module helps to create date and date-time fields that use the
27
+ # Unobtrusive Date-Picker Javascript Widget.
28
+ #
29
+ # They also use the 12-hour AM/PM time format.
30
+ #
31
+ module UnobtrusiveDatePickerHelper
32
+
33
+ ##
34
+ # Creates the date picker with the calendar widget.
35
+ #
36
+ def unobtrusive_date_picker(object_name, method, options = {}, html_options = {})
37
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_datepicker_date_select_tag(options, html_options)
38
+ end
39
+
40
+ ##
41
+ # Creates the date-time picker with the calendar widget, and AM/PM select.
42
+ #
43
+ def unobtrusive_datetime_picker(object_name, method, options = {}, html_options = {})
44
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_datepicker_datetime_select_tag(options, html_options)
45
+ end
46
+
47
+ ##
48
+ # Creates the date picker with the calendar widget.
49
+ #
50
+ def unobtrusive_date_text_picker(object_name, method, options = {}, html_options = {})
51
+ ActionView::Helpers::InstanceTag.new(object_name, method, self, options.delete(:object)).to_datepicker_text_tag(options, html_options)
52
+ end
53
+
54
+ def unobtrusive_datetime_picker_tags(datetime = Time.current, options = {}, html_options = {})
55
+ datetime ||= Time.current
56
+ DateTimePickerSelector.new(datetime, options.merge(:twelve_hour => true), html_options).select_datetime
57
+ end
58
+
59
+ def unobtrusive_date_picker_tags(date = Date.current, options = {}, html_options = {})
60
+ date ||= Date.current
61
+ DateTimePickerSelector.new(date, options, html_options).select_date
62
+ end
63
+
64
+ ##
65
+ # Creates the text field based date picker with the calendar widget without a model object.
66
+ #
67
+ def unobtrusive_date_text_picker_tag(name, date = Date.current, options = {}, html_options = {})
68
+ date ||= Date.current
69
+ options = merge_defaults_for_text_picker(options)
70
+ DateTimePickerSelector.new(date, options, html_options).text_date_picker(name)
71
+ end
72
+
73
+ private
74
+ def merge_defaults_for_text_picker(options)
75
+ defaults = {:format => 'm-d-y', :divider => 'slash'}
76
+ options = defaults.merge(options)
77
+ end
78
+
79
+ end
80
+
81
+ module AssetTagHelper
82
+ ##
83
+ # This will add the necessary <link> and <script> tags to include the necessary stylesheet and
84
+ # javascripts.
85
+ #
86
+ def unobtrusive_datepicker_includes(options = {})
87
+ tags = []
88
+ tags << javascript_include_tag('datepicker', options)
89
+ tags << stylesheet_link_tag('datepicker', options)
90
+ tags * "\n"
91
+ end
92
+ end
93
+
94
+ module OptionParser
95
+ protected
96
+ def parse_divider_option(option)
97
+ if DATEPICKER_DIVIDERS.keys.include?(option)
98
+ option
99
+ else
100
+ DATEPICKER_DIVIDERS.find {|name, value| option == value}.first
101
+ end
102
+ end
103
+
104
+ def format_date_value_for_text_field(value, format, divider_option)
105
+ divider = DATEPICKER_DIVIDERS[parse_divider_option(divider_option)]
106
+ format_string = format.downcase.gsub(/(m|d)/, '%\1').gsub(/y/, '%Y').gsub('-', divider)
107
+ value.nil? ? '' : value.strftime(format_string)
108
+ end
109
+
110
+ def get_html_classes_for_datepicker(options, html_options_class, extra_class = nil)
111
+ html_classes = make_date_picker_class_options(options)
112
+ html_classes << extra_class if extra_class
113
+ html_options_class.blank? ?
114
+ html_classes.join(' ') : "#{html_options_class} #{html_classes.join(' ')}"
115
+ end
116
+
117
+ def make_date_picker_class_options(options)
118
+ html_classes = []
119
+
120
+ if options[:highlight_days]
121
+ highlight_days = parse_days_of_week(options[:highlight_days])
122
+ if !highlight_days.blank?
123
+ html_classes << "highlight-days-#{highlight_days}"
124
+ end
125
+ end
126
+
127
+ if options[:range_low]
128
+ range_low = parse_range_option(options[:range_low], 'low')
129
+ if !range_low.blank?
130
+ html_classes << range_low
131
+ end
132
+ end
133
+
134
+ if options[:range_high]
135
+ range_high = parse_range_option(options[:range_high], 'high')
136
+ if !range_high.blank?
137
+ html_classes << range_high
138
+ end
139
+ end
140
+
141
+ if options[:disable_days]
142
+ disable_days = parse_days_of_week(options[:disable_days])
143
+ if !disable_days.blank?
144
+ html_classes << "disable-days-#{disable_days}"
145
+ end
146
+ end
147
+
148
+ if options[:no_transparency]
149
+ html_classes << 'no-transparency'
150
+ end
151
+
152
+ if options[:format] && %W(d-m-y m-d-y y-m-d).include?(options[:format].downcase)
153
+ html_classes << "format-#{options[:format].downcase}"
154
+ end
155
+
156
+ if options[:divider]
157
+ html_classes << "divider-#{parse_divider_option(options[:divider])}"
158
+ end
159
+
160
+ html_classes
161
+ end
162
+
163
+ def parse_days_of_week(option)
164
+ if option.is_a? String
165
+ option
166
+ elsif option.is_a? Symbol
167
+ DATEPICKER_DAYS_OF_WEEK[option]
168
+ elsif option.is_a? Array
169
+ days = ''
170
+ option.each do |day|
171
+ days << DATEPICKER_DAYS_OF_WEEK[day]
172
+ end
173
+ days
174
+ end
175
+ end
176
+
177
+ def parse_range_option(option, direction)
178
+ if option.is_a? Symbol
179
+ case option
180
+ when :today
181
+ range_class = 'today'
182
+ when :tomorrow
183
+ range_class = Date.tomorrow.strftime(RANGE_DATE_FORMAT)
184
+ when :yesterday
185
+ range_class = Date.yesterday.strftime(RANGE_DATE_FORMAT)
186
+ end
187
+ elsif option.is_a? String
188
+ if !option.blank?
189
+ range_class = Date.parse(option).strftime(RANGE_DATE_FORMAT)
190
+ else
191
+ range_class = nil
192
+ end
193
+ elsif (option.is_a?(Date) || option.is_a?(DateTime) || option.is_a?(Time))
194
+ range_class = option.strftime(RANGE_DATE_FORMAT)
195
+ else
196
+ range_class = nil
197
+ end
198
+
199
+ if !range_class.blank?
200
+ range_class = 'range-' + direction + '-' + range_class
201
+ else
202
+ nil
203
+ end
204
+ end
205
+ end
206
+
207
+ class DateTimePickerSelector < ActionView::Helpers::DateTimeSelector
208
+ include ActionView::Helpers::FormTagHelper
209
+ include OptionParser
210
+
211
+ POSITION = {
212
+ :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5,
213
+ :second => 6, :ampm => 7
214
+ }
215
+ # XXX would like to do this, but it's frozen
216
+ # POSITION[:ampm] = 7
217
+
218
+ # We give them negative values so can differentiate between normal
219
+ # date/time values. The way the multi param stuff works, from what I
220
+ # can see, results in a variable number of fields (if you tell it to
221
+ # include seconds, for example). So we expect the AM/PM field, if
222
+ # present, to be last and have a negative value.
223
+ AM = -1
224
+ PM = -2
225
+
226
+ def text_date_picker(name)
227
+ value = format_date_value_for_text_field(@datetime, @options[:format], @options[:divider])
228
+ @html_options[:class] = get_html_classes_for_datepicker(@options, @html_options[:class])
229
+ text_field_tag(name, value, @html_options)
230
+ end
231
+
232
+ def select_hour_with_ampm
233
+ unless @options[:twelve_hour]
234
+ return select_hour_without_ampm
235
+ end
236
+
237
+ if @options[:use_hidden] || @options[:discard_hour]
238
+ build_hidden(:hour, hour12)
239
+ else
240
+ build_options_and_select(:hour, hour12, :start => 1, :end => 12)
241
+ end
242
+ end
243
+
244
+ alias_method_chain :select_hour, :ampm
245
+
246
+ def select_ampm
247
+ selected = hour < 12 ? AM : PM
248
+
249
+ # XXX i18n?
250
+ label = { AM => 'AM', PM => 'PM' }
251
+ ampm_options = []
252
+ [AM, PM].each do |meridiem|
253
+ option = { :value => meridiem }
254
+ option[:selected] = "selected" if selected == meridiem
255
+ ampm_options << content_tag(:option, label[meridiem], option) + "\n"
256
+ end
257
+ build_select(:ampm, ampm_options.join)
258
+ end
259
+
260
+ private
261
+ def build_selects_from_types_with_ampm(order)
262
+ order += [:ampm] if @options[:twelve_hour] and !order.include?(:ampm)
263
+ build_selects_from_types_without_ampm(order)
264
+ end
265
+
266
+ alias_method_chain :build_selects_from_types, :ampm
267
+
268
+ def hour12
269
+ h12 = hour % 12
270
+ h12 = 12 if h12 == 0
271
+ return h12
272
+ end
273
+
274
+ def build_select(type, select_options_as_html)
275
+ select_options = @html_options.merge(
276
+ :id => input_id_from_type(type, @html_options[:id]),
277
+ :name => input_name_from_type(type)
278
+ )
279
+ select_options.merge!(:disabled => 'disabled') if @options[:disabled]
280
+
281
+ if type.to_sym == :year
282
+ select_options[:class] = get_html_classes_for_datepicker(@options, select_options[:class], 'split-date')
283
+ end
284
+
285
+ select_html = "\n"
286
+ select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
287
+ select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
288
+ select_html << select_options_as_html.to_s
289
+
290
+ content_tag(:select, select_html, select_options) + "\n"
291
+ end
292
+
293
+ def build_hidden(type, value)
294
+ hidden_html_options = {
295
+ :type => "hidden",
296
+ :id => input_id_from_type(type, @html_options[:id]),
297
+ :name => input_name_from_type(type),
298
+ :value => value
299
+ }
300
+
301
+ if type.to_sym == :year
302
+ hidden_html_options[:class] = get_html_classes_for_datepicker(@options, hidden_html_options[:class], 'split-date')
303
+ end
304
+
305
+ tag(:input, hidden_html_options) + "\n"
306
+ end
307
+
308
+ def input_id_from_type(type, html_options_id = nil)
309
+ if html_options_id.blank?
310
+ prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
311
+ prefix += "_#{@options[:index]}" if @options.has_key?(:index)
312
+ prefix += "_#{@options[:field_name]}" if @options.has_key?(:field_name)
313
+ else
314
+ prefix = html_options_id
315
+ end
316
+ case type.to_sym
317
+ when :year
318
+ prefix
319
+ when :month
320
+ prefix + '-mm'
321
+ when :day
322
+ prefix + '-dd'
323
+ else
324
+ super(type)
325
+ end
326
+ end
327
+
328
+ end
329
+
330
+ end
331
+ # /UnobtrusiveDatePicker
332
+
333
+ module ActionView # :nodoc: all
334
+ module Helpers
335
+ class InstanceTag
336
+ include UnobtrusiveDatePicker::UnobtrusiveDatePickerHelper
337
+ include UnobtrusiveDatePicker::OptionParser
338
+
339
+ def to_datepicker_date_select_tag(options = {}, html_options = {})
340
+ datepicker_selector(options, html_options).select_date
341
+ end
342
+
343
+ def to_datepicker_datetime_select_tag(options = {}, html_options = {})
344
+ datepicker_selector(options.merge(:twelve_hour => true), html_options).select_datetime
345
+ end
346
+
347
+ def to_datepicker_text_tag(options = {}, html_options = {})
348
+ options = merge_defaults_for_text_picker(options)
349
+ html_options[:class] = get_html_classes_for_datepicker(options, html_options[:class])
350
+ html_options[:value] = format_date_value_for_text_field(value(object), options[:format], options[:divider])
351
+ to_input_field_tag('text', html_options)
352
+ end
353
+
354
+ private
355
+ def datepicker_selector(options, html_options)
356
+ datetime = value(object) || default_datetime(options)
357
+
358
+ options = options.dup
359
+ options[:field_name] = @method_name
360
+ options[:include_position] = true
361
+ options[:prefix] ||= @object_name
362
+ options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
363
+ options[:datetime_separator] ||= ' &mdash; '
364
+ options[:time_separator] ||= ' : '
365
+
366
+ UnobtrusiveDatePicker::DateTimePickerSelector.new(datetime, options.merge(:tag => true), html_options)
367
+ end
368
+ end
369
+ end
370
+ end
371
+
372
+ module ActionView::Helpers::PrototypeHelper
373
+ class JavaScriptGenerator
374
+ module GeneratorMethods
375
+ def unobtrusive_date_picker_create(id = nil)
376
+ if id
377
+ call "datePickerController.create", "$(#{id})"
378
+ else
379
+ record "datePickerController.create"
380
+ end
381
+ end
382
+
383
+ def unobtrusive_date_picker_cleanup(id = nil)
384
+ record "datePickerController.cleanUp"
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+
391
+ module ActionView # :nodoc: all
392
+ module Helpers
393
+ class FormBuilder
394
+ def unobtrusive_date_picker(method, options = {}, html_options = {})
395
+ @template.unobtrusive_date_picker(@object_name, method, objectify_options(options), html_options)
396
+ end
397
+
398
+ def unobtrusive_date_text_picker(method, options = {}, html_options = {})
399
+ @template.unobtrusive_date_text_picker(@object_name, method, objectify_options(options), html_options)
400
+ end
401
+
402
+ def unobtrusive_datetime_picker(method, options = {}, html_options = {})
403
+ @template.unobtrusive_datetime_picker(@object_name, method, objectify_options(options), html_options)
404
+ end
405
+ end
406
+ end
407
+ end