gbdev-calendar_date_select 1.11.20080824
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.
- data/CHANGELOG +204 -0
- data/MIT-LICENSE +20 -0
- data/README +30 -0
- data/calendar_date_select.gemspec +14 -0
- data/init.rb +16 -0
- data/js_test/functional/cds_test.html +334 -0
- data/js_test/prototype.js +4184 -0
- data/js_test/test.css +40 -0
- data/js_test/unit/cds_helper_methods.html +46 -0
- data/js_test/unittest.js +564 -0
- data/lib/calendar_date_select.rb +222 -0
- data/lib/gem_init.rb +3 -0
- data/lib/includes_helper.rb +38 -0
- data/public/blank_iframe.html +2 -0
- data/public/images/calendar_date_select/calendar.gif +0 -0
- data/public/javascripts/calendar_date_select/calendar_date_select.js +430 -0
- data/public/javascripts/calendar_date_select/format_american.js +34 -0
- data/public/javascripts/calendar_date_select/format_db.js +27 -0
- data/public/javascripts/calendar_date_select/format_euro_24hr.js +7 -0
- data/public/javascripts/calendar_date_select/format_euro_24hr_ymd.js +7 -0
- data/public/javascripts/calendar_date_select/format_finnish.js +24 -0
- data/public/javascripts/calendar_date_select/format_hyphen_ampm.js +36 -0
- data/public/javascripts/calendar_date_select/format_iso_date.js +46 -0
- data/public/javascripts/calendar_date_select/format_italian.js +24 -0
- data/public/javascripts/calendar_date_select/locale/fi.js +10 -0
- data/public/javascripts/calendar_date_select/locale/pl.js +10 -0
- data/public/javascripts/calendar_date_select/locale/pt.js +10 -0
- data/public/stylesheets/calendar_date_select/blue.css +130 -0
- data/public/stylesheets/calendar_date_select/default.css +135 -0
- data/public/stylesheets/calendar_date_select/plain.css +128 -0
- data/public/stylesheets/calendar_date_select/red.css +135 -0
- data/public/stylesheets/calendar_date_select/silver.css +133 -0
- data/test/functional/calendar_date_select_test.rb +157 -0
- data/test/functional/helper_methods_test.rb +15 -0
- data/test/test_helper.rb +26 -0
- metadata +89 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
class CalendarDateSelect
|
2
|
+
FORMATS = {
|
3
|
+
:natural => {
|
4
|
+
:date => "%B %d, %Y",
|
5
|
+
:time => " %I:%M %p"
|
6
|
+
},
|
7
|
+
:hyphen_ampm => {
|
8
|
+
:date => "%Y-%m-%d",
|
9
|
+
:time => " %I:%M %p",
|
10
|
+
:javascript_include => "format_hyphen_ampm"
|
11
|
+
},
|
12
|
+
:iso_date => {
|
13
|
+
:date => "%Y-%m-%d",
|
14
|
+
:time => " %H:%M",
|
15
|
+
:javascript_include => "format_iso_date"
|
16
|
+
},
|
17
|
+
:finnish => {
|
18
|
+
:date => "%d.%m.%Y",
|
19
|
+
:time => " %H:%M",
|
20
|
+
:javascript_include => "format_finnish"
|
21
|
+
},
|
22
|
+
:american => {
|
23
|
+
:date => "%m/%d/%Y",
|
24
|
+
:time => " %I:%M %p",
|
25
|
+
:javascript_include => "format_american"
|
26
|
+
},
|
27
|
+
:euro_24hr => {
|
28
|
+
:date => "%d %B %Y",
|
29
|
+
:time => " %H:%M",
|
30
|
+
:javascript_include => "format_euro_24hr"
|
31
|
+
},
|
32
|
+
:euro_24hr_ymd => {
|
33
|
+
:date => "%Y.%m.%d",
|
34
|
+
:time => " %H:%M",
|
35
|
+
:javascript_include => "format_euro_24hr_ymd"
|
36
|
+
},
|
37
|
+
:italian => {
|
38
|
+
:date => "%d/%m/%Y",
|
39
|
+
:time => " %H:%M",
|
40
|
+
:javascript_include => "format_italian"
|
41
|
+
},
|
42
|
+
:db => {
|
43
|
+
:date => "%Y-%m-%d",
|
44
|
+
:time => "%H:%M",
|
45
|
+
:javascript_include => "format_db"
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
cattr_accessor :image
|
50
|
+
@@image = "calendar_date_select/calendar.gif"
|
51
|
+
|
52
|
+
cattr_reader :format
|
53
|
+
@@format = FORMATS[:natural]
|
54
|
+
|
55
|
+
class << self
|
56
|
+
def format=(format)
|
57
|
+
raise "CalendarDateSelect: Unrecognized format specification: #{format}" unless FORMATS.has_key?(format)
|
58
|
+
@@format = FORMATS[format]
|
59
|
+
end
|
60
|
+
|
61
|
+
def javascript_format_include
|
62
|
+
@@format[:javascript_include] && "calendar_date_select/#{@@format[:javascript_include]}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def date_format_string(time=false)
|
66
|
+
@@format[:date] + ( time ? @@format[:time] : "" )
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_date(date)
|
70
|
+
if Date===date
|
71
|
+
date.strftime(date_format_string(false))
|
72
|
+
else
|
73
|
+
date.strftime(date_format_string(true))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_time?(value)
|
78
|
+
/[0-9]:[0-9]{2}/.match(value.to_s)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module FormHelper
|
83
|
+
def calendar_date_select_tag( name, value = nil, options = {})
|
84
|
+
calendar_options = calendar_date_select_process_options(options)
|
85
|
+
value = format_time(value, calendar_options)
|
86
|
+
|
87
|
+
calendar_options.delete(:format)
|
88
|
+
|
89
|
+
options[:id] ||= name
|
90
|
+
tag = calendar_options[:hidden] || calendar_options[:embedded] ?
|
91
|
+
hidden_field_tag(name, value, options) :
|
92
|
+
text_field_tag(name, value, options)
|
93
|
+
|
94
|
+
calendar_date_select_output(tag, calendar_options)
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_time(value, options = {})
|
98
|
+
if value.respond_to?("strftime")
|
99
|
+
if options[:format]
|
100
|
+
value = value.strftime(options[:format])
|
101
|
+
else
|
102
|
+
if options.has_key? :time
|
103
|
+
value = value.strftime(CalendarDateSelect.date_format_string(options[:time]))
|
104
|
+
else
|
105
|
+
value = CalendarDateSelect.format_date(value)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
value
|
110
|
+
end
|
111
|
+
|
112
|
+
# extracts any options passed into calendar date select, appropriating them to either the Javascript call or the html tag.
|
113
|
+
def calendar_date_select_process_options(options)
|
114
|
+
calendar_options = {}
|
115
|
+
callbacks = [:before_show, :before_close, :after_show, :after_close, :after_navigate]
|
116
|
+
for key in [:time, :valid_date_check, :embedded, :buttons, :format, :year_range, :month_year, :popup, :hidden, :minute_interval] + callbacks
|
117
|
+
calendar_options[key] = options.delete(key) if options.has_key?(key)
|
118
|
+
end
|
119
|
+
|
120
|
+
# if passing in mixed, pad it with single quotes
|
121
|
+
calendar_options[:time] = "'mixed'" if calendar_options[:time].to_s=="mixed"
|
122
|
+
calendar_options[:month_year] = "'#{calendar_options[:month_year]}'" if calendar_options[:month_year]
|
123
|
+
|
124
|
+
# if we are forcing the popup, automatically set the readonly property on the input control.
|
125
|
+
if calendar_options[:popup].to_s == "force"
|
126
|
+
calendar_options[:popup] = "'force'"
|
127
|
+
options[:readonly] = true
|
128
|
+
end
|
129
|
+
|
130
|
+
if (vdc=calendar_options.delete(:valid_date_check))
|
131
|
+
if vdc.include?(";") || vdc.include?("function")
|
132
|
+
throw ":valid_date_check function is missing a 'return' statement. Try something like: :valid_date_check => 'if (date > new(Date)) return true; else return false;'" unless vdc.include?("return");
|
133
|
+
end
|
134
|
+
|
135
|
+
vdc = "return(#{vdc})" unless vdc.include?("return")
|
136
|
+
vdc = "function(date) { #{vdc} }" unless vdc.include?("function")
|
137
|
+
calendar_options[:valid_date_check] = vdc
|
138
|
+
end
|
139
|
+
|
140
|
+
calendar_options[:popup_by] ||= "this" if calendar_options[:hidden]
|
141
|
+
|
142
|
+
# surround any callbacks with a function, if not already done so
|
143
|
+
for key in callbacks
|
144
|
+
calendar_options[key] = "function(param) { #{calendar_options[key]} }" unless calendar_options[key].include?("function") if calendar_options[key]
|
145
|
+
end
|
146
|
+
|
147
|
+
calendar_options[:year_range] = format_year_range(calendar_options[:year_range] || 10)
|
148
|
+
calendar_options
|
149
|
+
end
|
150
|
+
|
151
|
+
def calendar_date_select(object, method, options={})
|
152
|
+
obj = options[:object] || instance_variable_get("@#{object}")
|
153
|
+
|
154
|
+
if !options.include?(:time) && obj.class.respond_to?("columns_hash")
|
155
|
+
column_type = (obj.class.columns_hash[method.to_s].type rescue nil)
|
156
|
+
options[:time] = true if column_type == :datetime
|
157
|
+
end
|
158
|
+
|
159
|
+
use_time = options[:time]
|
160
|
+
|
161
|
+
if options[:time].to_s=="mixed"
|
162
|
+
use_time = false if Date===(obj.respond_to?(method) && obj.send(method))
|
163
|
+
end
|
164
|
+
|
165
|
+
calendar_options = calendar_date_select_process_options(options)
|
166
|
+
|
167
|
+
options[:value] ||=
|
168
|
+
if(obj.respond_to?(method) && obj.send(method).respond_to?(:strftime))
|
169
|
+
obj.send(method).strftime(CalendarDateSelect.date_format_string(use_time))
|
170
|
+
elsif obj.respond_to?("#{method}_before_type_cast")
|
171
|
+
obj.send("#{method}_before_type_cast")
|
172
|
+
elsif obj.respond_to?(method)
|
173
|
+
obj.send(method).to_s
|
174
|
+
else
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
178
|
+
tag = ActionView::Helpers::InstanceTag.new(object, method, self, nil, options.delete(:object))
|
179
|
+
calendar_date_select_output(
|
180
|
+
tag.to_input_field_tag( (calendar_options[:hidden] || calendar_options[:embedded]) ? "hidden" : "text", options),
|
181
|
+
calendar_options
|
182
|
+
)
|
183
|
+
end
|
184
|
+
|
185
|
+
def calendar_date_select_output(input, calendar_options = {})
|
186
|
+
out = input
|
187
|
+
if calendar_options[:embedded]
|
188
|
+
uniq_id = "cds_placeholder_#{(rand*100000).to_i}"
|
189
|
+
# we need to be able to locate the target input element, so lets stick an invisible span tag here we can easily locate
|
190
|
+
out << content_tag(:span, nil, :style => "display: none; position: absolute;", :id => uniq_id)
|
191
|
+
|
192
|
+
out << javascript_tag("new CalendarDateSelect( $('#{uniq_id}').previous(), #{options_for_javascript(calendar_options)} ); ")
|
193
|
+
else
|
194
|
+
out << " "
|
195
|
+
|
196
|
+
out << image_tag(CalendarDateSelect.image,
|
197
|
+
:onclick => "new CalendarDateSelect( $(this).previous(), #{options_for_javascript(calendar_options)} );",
|
198
|
+
:style => 'border:0px; cursor:pointer;')
|
199
|
+
end
|
200
|
+
|
201
|
+
out
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
def format_year_range(year) # nodoc
|
206
|
+
return year unless year.respond_to?(:first)
|
207
|
+
return "[#{year.first}, #{year.last}]" unless year.first.respond_to?(:strftime)
|
208
|
+
return "[#{year.first.year}, #{year.last.year}]"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
module ActionView
|
215
|
+
module Helpers
|
216
|
+
class FormBuilder
|
217
|
+
def calendar_date_select(method, options = {})
|
218
|
+
@template.calendar_date_select(@object_name, method, options.merge(:object => @object))
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/gem_init.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class CalendarDateSelect
|
2
|
+
module IncludesHelper
|
3
|
+
def calendar_date_select_stylesheets(options = {})
|
4
|
+
options.assert_valid_keys(:style, :format, :locale)
|
5
|
+
|
6
|
+
style = options[:style]
|
7
|
+
cds_css_file = style ? "calendar_date_select/#{style}" : "calendar_date_select/default"
|
8
|
+
return cds_css_file
|
9
|
+
end
|
10
|
+
|
11
|
+
def calendar_date_select_javascripts(options = {})
|
12
|
+
options.assert_valid_keys(:style, :format, :locale)
|
13
|
+
|
14
|
+
style = options[:style]
|
15
|
+
locale = options[:locale]
|
16
|
+
cds_css_file = style ? "calendar_date_select/#{style}" : "calendar_date_select/default"
|
17
|
+
|
18
|
+
output = []
|
19
|
+
output << "calendar_date_select/calendar_date_select"
|
20
|
+
output << "calendar_date_select/locale/#{locale}" if locale
|
21
|
+
output << CalendarDateSelect.javascript_format_include if CalendarDateSelect.javascript_format_include
|
22
|
+
return output
|
23
|
+
end
|
24
|
+
|
25
|
+
def calendar_date_select_includes(*args)
|
26
|
+
return "" if @cds_already_included
|
27
|
+
@cds_already_included=true
|
28
|
+
|
29
|
+
options = (Hash === args.last) ? args.pop : {}
|
30
|
+
options.assert_valid_keys(:style, :format, :locale)
|
31
|
+
options[:style] ||= args.shift
|
32
|
+
|
33
|
+
js = javascript_include_tag(*calendar_date_select_javascripts(options))
|
34
|
+
css = stylesheet_link_tag(*calendar_date_select_stylesheets(options))
|
35
|
+
"#{js}\n#{css}\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
Binary file
|
@@ -0,0 +1,430 @@
|
|
1
|
+
// CalendarDateSelect version 1.10.11 - a prototype based date picker
|
2
|
+
// Questions, comments, bugs? - see the project page: http://code.google.com/p/calendardateselect
|
3
|
+
if (typeof Prototype == 'undefined') alert("CalendarDateSelect Error: Prototype could not be found. Please make sure that your application's layout includes prototype.js (.g. <%= javascript_include_tag :defaults %>) *before* it includes calendar_date_select.js (.g. <%= calendar_date_select_includes %>).");
|
4
|
+
if (Prototype.Version < "1.6") alert("Prototype 1.6.0 is required. If using earlier version of prototype, please use calendar_date_select version 1.8.3");
|
5
|
+
|
6
|
+
Element.addMethods({
|
7
|
+
purgeChildren: function(element) { $A(element.childNodes).each(function(e){$(e).remove();}); },
|
8
|
+
build: function(element, type, options, style) {
|
9
|
+
var newElement = Element.buildAndAppend(type, options, style);
|
10
|
+
element.appendChild(newElement);
|
11
|
+
return newElement;
|
12
|
+
}
|
13
|
+
});
|
14
|
+
|
15
|
+
Element.buildAndAppend = function(type, options, style)
|
16
|
+
{
|
17
|
+
var e = $(document.createElement(type));
|
18
|
+
$H(options).each(function(pair) { eval("e." + pair.key + " = pair.value" ); });
|
19
|
+
if (style)
|
20
|
+
$H(style).each(function(pair) { eval("e.style." + pair.key + " = pair.value" ); });
|
21
|
+
return e;
|
22
|
+
};
|
23
|
+
nil = null;
|
24
|
+
|
25
|
+
Date.one_day = 24*60*60*1000;
|
26
|
+
Date.weekdays = $w("S M T W T F S");
|
27
|
+
Date.first_day_of_week = 0;
|
28
|
+
Date.months = $w("January February March April May June July August September October November December" );
|
29
|
+
Date.padded2 = function(hour) { var padded2 = parseInt(hour, 10); if (hour < 10) padded2 = "0" + padded2; return padded2; }
|
30
|
+
Date.prototype.getPaddedMinutes = function() { return Date.padded2(this.getMinutes()); }
|
31
|
+
Date.prototype.getAMPMHour = function() { var hour = this.getHours(); return (hour == 0) ? 12 : (hour > 12 ? hour - 12 : hour ) }
|
32
|
+
Date.prototype.getAMPM = function() { return (this.getHours() < 12) ? "AM" : "PM"; }
|
33
|
+
Date.prototype.stripTime = function() { return new Date(this.getFullYear(), this.getMonth(), this.getDate());};
|
34
|
+
Date.prototype.daysDistance = function(compare_date) { return Math.round((compare_date - this) / Date.one_day); };
|
35
|
+
Date.prototype.toFormattedString = function(include_time){
|
36
|
+
var hour, str;
|
37
|
+
str = Date.months[this.getMonth()] + " " + this.getDate() + ", " + this.getFullYear();
|
38
|
+
|
39
|
+
if (include_time) { hour = this.getHours(); str += " " + this.getAMPMHour() + ":" + this.getPaddedMinutes() + " " + this.getAMPM() }
|
40
|
+
return str;
|
41
|
+
}
|
42
|
+
Date.parseFormattedString = function(string) { return new Date(string);}
|
43
|
+
Math.floor_to_interval = function(n, i) { return Math.floor(n/i) * i;}
|
44
|
+
window.f_height = function() { return( [window.innerHeight ? window.innerHeight : null, document.documentElement ? document.documentElement.clientHeight : null, document.body ? document.body.clientHeight : null].select(function(x){return x>0}).first()||0); }
|
45
|
+
window.f_scrollTop = function() { return ([window.pageYOffset ? window.pageYOffset : null, document.documentElement ? document.documentElement.scrollTop : null, document.body ? document.body.scrollTop : null].select(function(x){return x>0}).first()||0 ); }
|
46
|
+
|
47
|
+
_translations = {
|
48
|
+
"OK": "OK",
|
49
|
+
"Now": "Now",
|
50
|
+
"Today": "Today"
|
51
|
+
}
|
52
|
+
SelectBox = Class.create();
|
53
|
+
SelectBox.prototype = {
|
54
|
+
initialize: function(parent_element, values, html_options, style_options) {
|
55
|
+
this.element = $(parent_element).build("select", html_options, style_options);
|
56
|
+
this.populate(values);
|
57
|
+
},
|
58
|
+
populate: function(values) {
|
59
|
+
this.element.purgeChildren();
|
60
|
+
var that = this; $A(values).each(function(pair) { if (typeof(pair)!="object") {pair = [pair, pair]}; that.element.build("option", { value: pair[1], innerHTML: pair[0]}) });
|
61
|
+
},
|
62
|
+
setValue: function(value) {
|
63
|
+
var e = this.element;
|
64
|
+
var matched = false;
|
65
|
+
$R(0, e.options.length - 1 ).each(function(i) { if(e.options[i].value==value.toString()) {e.selectedIndex = i; matched = true;}; } );
|
66
|
+
return matched;
|
67
|
+
},
|
68
|
+
getValue: function() { return $F(this.element)}
|
69
|
+
}
|
70
|
+
CalendarDateSelect = Class.create();
|
71
|
+
CalendarDateSelect.prototype = {
|
72
|
+
initialize: function(target_element, options) {
|
73
|
+
this.target_element = $(target_element); // make sure it's an element, not a string
|
74
|
+
if (!this.target_element) { alert("Target element " + target_element + " not found!"); return false;}
|
75
|
+
if (this.target_element.tagName != "INPUT") this.target_element = this.target_element.down("INPUT")
|
76
|
+
|
77
|
+
this.target_element.calendar_date_select = this;
|
78
|
+
this.last_click_at = 0;
|
79
|
+
// initialize the date control
|
80
|
+
this.options = $H({
|
81
|
+
embedded: false,
|
82
|
+
popup: nil,
|
83
|
+
time: false,
|
84
|
+
buttons: true,
|
85
|
+
year_range: 10,
|
86
|
+
close_on_click: nil,
|
87
|
+
minute_interval: 5,
|
88
|
+
popup_by: this.target_element,
|
89
|
+
month_year: "dropdowns",
|
90
|
+
onchange: this.target_element.onchange,
|
91
|
+
valid_date_check: nil
|
92
|
+
}).merge(options || {});
|
93
|
+
this.use_time = this.options.get("time");
|
94
|
+
this.parseDate();
|
95
|
+
this.callback("before_show")
|
96
|
+
this.initCalendarDiv();
|
97
|
+
if(!this.options.get("embedded")) {
|
98
|
+
this.positionCalendarDiv()
|
99
|
+
// set the click handler to check if a user has clicked away from the document
|
100
|
+
Event.observe(document, "mousedown", this.closeIfClickedOut_handler = this.closeIfClickedOut.bindAsEventListener(this));
|
101
|
+
Event.observe(document, "keypress", this.keyPress_handler = this.keyPress.bindAsEventListener(this));
|
102
|
+
}
|
103
|
+
this.callback("after_show")
|
104
|
+
},
|
105
|
+
positionCalendarDiv: function() {
|
106
|
+
var above = false;
|
107
|
+
var c_pos = this.calendar_div.cumulativeOffset(), c_left = c_pos[0], c_top = c_pos[1], c_dim = this.calendar_div.getDimensions(), c_height = c_dim.height, c_width = c_dim.width;
|
108
|
+
var w_top = window.f_scrollTop(), w_height = window.f_height();
|
109
|
+
var e_dim = $(this.options.get("popup_by")).cumulativeOffset(), e_top = e_dim[1], e_left = e_dim[0], e_height = $(this.options.get("popup_by")).getDimensions().height, e_bottom = e_top + e_height;
|
110
|
+
|
111
|
+
if ( (( e_bottom + c_height ) > (w_top + w_height)) && ( e_bottom - c_height > w_top )) above = true;
|
112
|
+
var left_px = e_left.toString() + "px", top_px = (above ? (e_top - c_height ) : ( e_top + e_height )).toString() + "px";
|
113
|
+
|
114
|
+
this.calendar_div.style.left = left_px; this.calendar_div.style.top = top_px;
|
115
|
+
|
116
|
+
this.calendar_div.setStyle({visibility:""});
|
117
|
+
|
118
|
+
// draw an iframe behind the calendar -- ugly hack to make IE 6 happy
|
119
|
+
if(navigator.appName=="Microsoft Internet Explorer") this.iframe = $(document.body).build("iframe", {src: "javascript:false", className: "ie6_blocker"}, { left: left_px, top: top_px, height: c_height.toString()+"px", width: c_width.toString()+"px", border: "0px"})
|
120
|
+
},
|
121
|
+
initCalendarDiv: function() {
|
122
|
+
if (this.options.get("embedded")) {
|
123
|
+
var parent = this.target_element.parentNode;
|
124
|
+
var style = {}
|
125
|
+
} else {
|
126
|
+
var parent = document.body
|
127
|
+
var style = { position:"absolute", visibility: "hidden", left:0, top:0 }
|
128
|
+
}
|
129
|
+
this.calendar_div = $(parent).build('div', {className: "calendar_date_select"}, style);
|
130
|
+
|
131
|
+
var that = this;
|
132
|
+
// create the divs
|
133
|
+
$w("top header body buttons footer bottom").each(function(name) {
|
134
|
+
eval("var " + name + "_div = that." + name + "_div = that.calendar_div.build('div', { className: 'cds_"+name+"' }, { clear: 'left'} ); ");
|
135
|
+
});
|
136
|
+
|
137
|
+
this.initHeaderDiv();
|
138
|
+
this.initButtonsDiv();
|
139
|
+
this.initCalendarGrid();
|
140
|
+
this.updateFooter(" ");
|
141
|
+
|
142
|
+
this.refresh();
|
143
|
+
this.setUseTime(this.use_time);
|
144
|
+
},
|
145
|
+
initHeaderDiv: function() {
|
146
|
+
var header_div = this.header_div;
|
147
|
+
this.close_button = header_div.build("a", { innerHTML: "x", href:"#", onclick:function () { this.close(); return false; }.bindAsEventListener(this), className: "close" });
|
148
|
+
this.next_month_button = header_div.build("a", { innerHTML: ">", href:"#", onclick:function () { this.navMonth(this.date.getMonth() + 1 ); return false; }.bindAsEventListener(this), className: "next" });
|
149
|
+
this.prev_month_button = header_div.build("a", { innerHTML: "<", href:"#", onclick:function () { this.navMonth(this.date.getMonth() - 1 ); return false; }.bindAsEventListener(this), className: "prev" });
|
150
|
+
|
151
|
+
if (this.options.get("month_year")=="dropdowns") {
|
152
|
+
this.month_select = new SelectBox(header_div, $R(0,11).map(function(m){return [Date.months[m], m]}), {className: "month", onchange: function () { this.navMonth(this.month_select.getValue()) }.bindAsEventListener(this)});
|
153
|
+
this.year_select = new SelectBox(header_div, [], {className: "year", onchange: function () { this.navYear(this.year_select.getValue()) }.bindAsEventListener(this)});
|
154
|
+
this.populateYearRange();
|
155
|
+
} else {
|
156
|
+
this.month_year_label = header_div.build("span")
|
157
|
+
}
|
158
|
+
},
|
159
|
+
initCalendarGrid: function() {
|
160
|
+
var body_div = this.body_div;
|
161
|
+
this.calendar_day_grid = [];
|
162
|
+
var days_table = body_div.build("table", { cellPadding: "0px", cellSpacing: "0px", width: "100%" })
|
163
|
+
// make the weekdays!
|
164
|
+
var weekdays_row = days_table.build("thead").build("tr");
|
165
|
+
Date.weekdays.each( function(weekday) {
|
166
|
+
weekdays_row.build("th", {innerHTML: weekday});
|
167
|
+
});
|
168
|
+
|
169
|
+
var days_tbody = days_table.build("tbody")
|
170
|
+
// Make the days!
|
171
|
+
var row_number = 0, weekday;
|
172
|
+
for(var cell_index = 0; cell_index<42; cell_index++)
|
173
|
+
{
|
174
|
+
weekday = (cell_index+Date.first_day_of_week ) % 7;
|
175
|
+
if ( cell_index % 7==0 ) days_row = days_tbody.build("tr", {className: 'row_'+row_number++});
|
176
|
+
(this.calendar_day_grid[cell_index] = days_row.build("td", {
|
177
|
+
calendar_date_select: this,
|
178
|
+
onmouseover: function () { this.calendar_date_select.dayHover(this); },
|
179
|
+
onmouseout: function () { this.calendar_date_select.dayHoverOut(this) },
|
180
|
+
onclick: function() { this.calendar_date_select.updateSelectedDate(this, true); },
|
181
|
+
className: (weekday==0) || (weekday==6) ? " weekend" : "" //clear the class
|
182
|
+
},
|
183
|
+
{ cursor: "pointer" }
|
184
|
+
)).build("div");
|
185
|
+
this.calendar_day_grid[cell_index];
|
186
|
+
}
|
187
|
+
},
|
188
|
+
initButtonsDiv: function()
|
189
|
+
{
|
190
|
+
var buttons_div = this.buttons_div;
|
191
|
+
if (this.options.get("time"))
|
192
|
+
{
|
193
|
+
var blank_time = $A(this.options.get("time")=="mixed" ? [[" - ", ""]] : []);
|
194
|
+
buttons_div.build("span", {innerHTML:"@", className: "at_sign"});
|
195
|
+
|
196
|
+
var t = new Date();
|
197
|
+
this.hour_select = new SelectBox(buttons_div,
|
198
|
+
blank_time.concat($R(0,23).map(function(x) {t.setHours(x); return $A([t.getAMPMHour()+ " " + t.getAMPM(),x])} )),
|
199
|
+
{
|
200
|
+
calendar_date_select: this,
|
201
|
+
onchange: function() { this.calendar_date_select.updateSelectedDate( { hour: this.value });},
|
202
|
+
className: "hour"
|
203
|
+
}
|
204
|
+
);
|
205
|
+
buttons_div.build("span", {innerHTML:":", className: "seperator"});
|
206
|
+
var that = this;
|
207
|
+
this.minute_select = new SelectBox(buttons_div,
|
208
|
+
blank_time.concat($R(0,59).select(function(x){return (x % that.options.get('minute_interval')==0)}).map(function(x){ return $A([ Date.padded2(x), x]); } ) ),
|
209
|
+
{
|
210
|
+
calendar_date_select: this,
|
211
|
+
onchange: function() { this.calendar_date_select.updateSelectedDate( {minute: this.value }) },
|
212
|
+
className: "minute"
|
213
|
+
}
|
214
|
+
);
|
215
|
+
|
216
|
+
} else if (! this.options.get("buttons")) buttons_div.remove();
|
217
|
+
|
218
|
+
if (this.options.get("buttons")) {
|
219
|
+
buttons_div.build("span", {innerHTML: " "});
|
220
|
+
if (this.options.get("time")=="mixed" || !this.options.get("time")) b = buttons_div.build("a", {
|
221
|
+
innerHTML: _translations["Today"],
|
222
|
+
href: "#",
|
223
|
+
onclick: function() {this.today(false); return false;}.bindAsEventListener(this)
|
224
|
+
});
|
225
|
+
|
226
|
+
if (this.options.get("time")=="mixed") buttons_div.build("span", {innerHTML: " | ", className:"button_seperator"})
|
227
|
+
|
228
|
+
if (this.options.get("time")) b = buttons_div.build("a", {
|
229
|
+
innerHTML: _translations["Now"],
|
230
|
+
href: "#",
|
231
|
+
onclick: function() {this.today(true); return false}.bindAsEventListener(this)
|
232
|
+
});
|
233
|
+
|
234
|
+
if (!this.options.get("embedded"))
|
235
|
+
{
|
236
|
+
buttons_div.build("span", {innerHTML: " "});
|
237
|
+
buttons_div.build("a", { innerHTML: _translations["OK"], href: "#", onclick: function() {this.close(); return false;}.bindAsEventListener(this) });
|
238
|
+
}
|
239
|
+
}
|
240
|
+
},
|
241
|
+
refresh: function ()
|
242
|
+
{
|
243
|
+
this.refreshMonthYear();
|
244
|
+
this.refreshCalendarGrid();
|
245
|
+
|
246
|
+
this.setSelectedClass();
|
247
|
+
this.updateFooter();
|
248
|
+
},
|
249
|
+
refreshCalendarGrid: function () {
|
250
|
+
this.beginning_date = new Date(this.date).stripTime();
|
251
|
+
this.beginning_date.setDate(1);
|
252
|
+
this.beginning_date.setHours(12); // Prevent daylight savings time boundaries from showing a duplicate day
|
253
|
+
var pre_days = this.beginning_date.getDay() // draw some days before the fact
|
254
|
+
if (pre_days < 3) pre_days += 7;
|
255
|
+
this.beginning_date.setDate(1 - pre_days + Date.first_day_of_week);
|
256
|
+
|
257
|
+
var iterator = new Date(this.beginning_date);
|
258
|
+
|
259
|
+
var today = new Date().stripTime();
|
260
|
+
var this_month = this.date.getMonth();
|
261
|
+
vdc = this.options.get("valid_date_check");
|
262
|
+
for (var cell_index = 0;cell_index<42; cell_index++)
|
263
|
+
{
|
264
|
+
day = iterator.getDate(); month = iterator.getMonth();
|
265
|
+
cell = this.calendar_day_grid[cell_index];
|
266
|
+
Element.remove(cell.childNodes[0]); div = cell.build("div", {innerHTML:day});
|
267
|
+
if (month!=this_month) div.className = "other";
|
268
|
+
cell.day = day; cell.month = month; cell.year = iterator.getFullYear();
|
269
|
+
if (vdc) { if (vdc(iterator.stripTime())) cell.removeClassName("disabled"); else cell.addClassName("disabled") };
|
270
|
+
iterator.setDate( day + 1);
|
271
|
+
}
|
272
|
+
|
273
|
+
if (this.today_cell) this.today_cell.removeClassName("today");
|
274
|
+
|
275
|
+
if ( $R( 0, 41 ).include(days_until = this.beginning_date.stripTime().daysDistance(today)) ) {
|
276
|
+
this.today_cell = this.calendar_day_grid[days_until];
|
277
|
+
this.today_cell.addClassName("today");
|
278
|
+
}
|
279
|
+
},
|
280
|
+
refreshMonthYear: function() {
|
281
|
+
var m = this.date.getMonth();
|
282
|
+
var y = this.date.getFullYear();
|
283
|
+
// set the month
|
284
|
+
if (this.options.get("month_year") == "dropdowns")
|
285
|
+
{
|
286
|
+
this.month_select.setValue(m, false);
|
287
|
+
|
288
|
+
var e = this.year_select.element;
|
289
|
+
if (this.flexibleYearRange() && (!(this.year_select.setValue(y, false)) || e.selectedIndex <= 1 || e.selectedIndex >= e.options.length - 2 )) this.populateYearRange();
|
290
|
+
|
291
|
+
this.year_select.setValue(y);
|
292
|
+
|
293
|
+
} else {
|
294
|
+
this.month_year_label.update( Date.months[m] + " " + y.toString() );
|
295
|
+
}
|
296
|
+
},
|
297
|
+
populateYearRange: function() {
|
298
|
+
this.year_select.populate(this.yearRange().toArray());
|
299
|
+
},
|
300
|
+
yearRange: function() {
|
301
|
+
if (!this.flexibleYearRange())
|
302
|
+
return $R(this.options.get("year_range")[0], this.options.get("year_range")[1]);
|
303
|
+
|
304
|
+
var y = this.date.getFullYear();
|
305
|
+
return $R(y - this.options.get("year_range"), y + this.options.get("year_range"));
|
306
|
+
},
|
307
|
+
flexibleYearRange: function() { return (typeof(this.options.get("year_range")) == "number"); },
|
308
|
+
validYear: function(year) { if (this.flexibleYearRange()) { return true;} else { return this.yearRange().include(year);} },
|
309
|
+
dayHover: function(element) {
|
310
|
+
var hover_date = new Date(this.selected_date);
|
311
|
+
hover_date.setYear(element.year); hover_date.setMonth(element.month); hover_date.setDate(element.day);
|
312
|
+
this.updateFooter(hover_date.toFormattedString(this.use_time));
|
313
|
+
},
|
314
|
+
dayHoverOut: function(element) { this.updateFooter(); },
|
315
|
+
clearSelectedClass: function() {if (this.selected_cell) this.selected_cell.removeClassName("selected");},
|
316
|
+
setSelectedClass: function() {
|
317
|
+
if (!this.selection_made) return;
|
318
|
+
this.clearSelectedClass()
|
319
|
+
if ($R(0,42).include( days_until = this.beginning_date.stripTime().daysDistance(this.selected_date.stripTime()) )) {
|
320
|
+
this.selected_cell = this.calendar_day_grid[days_until];
|
321
|
+
this.selected_cell.addClassName("selected");
|
322
|
+
}
|
323
|
+
},
|
324
|
+
reparse: function() { this.parseDate(); this.refresh(); },
|
325
|
+
dateString: function() {
|
326
|
+
return (this.selection_made) ? this.selected_date.toFormattedString(this.use_time) : " ";
|
327
|
+
},
|
328
|
+
parseDate: function()
|
329
|
+
{
|
330
|
+
var value = $F(this.target_element).strip()
|
331
|
+
this.selection_made = (value != "");
|
332
|
+
this.date = value=="" ? NaN : Date.parseFormattedString(this.options.get("date") || value);
|
333
|
+
if (isNaN(this.date)) this.date = new Date();
|
334
|
+
if (!this.validYear(this.date.getFullYear())) this.date.setYear( (this.date.getFullYear() < this.yearRange().start) ? this.yearRange().start : this.yearRange().end);
|
335
|
+
this.selected_date = new Date(this.date);
|
336
|
+
this.use_time = /[0-9]:[0-9]{2}/.exec(value) ? true : false;
|
337
|
+
this.date.setDate(1);
|
338
|
+
},
|
339
|
+
updateFooter:function(text) { if (!text) text = this.dateString(); this.footer_div.purgeChildren(); this.footer_div.build("span", {innerHTML: text }); },
|
340
|
+
updateSelectedDate:function(partsOrElement, via_click) {
|
341
|
+
var parts = $H(partsOrElement);
|
342
|
+
if ((this.target_element.disabled || this.target_element.readOnly) && this.options.get("popup") != "force") return false;
|
343
|
+
if (parts.get("day")) {
|
344
|
+
var t_selected_date = this.selected_date, vdc = this.options.get("valid_date_check");
|
345
|
+
for (var x = 0; x<=3; x++) t_selected_date.setDate(parts.get("day"));
|
346
|
+
t_selected_date.setYear(parts.get("year"));
|
347
|
+
t_selected_date.setMonth(parts.get("month"));
|
348
|
+
|
349
|
+
if (vdc && ! vdc(t_selected_date.stripTime())) { return false; }
|
350
|
+
this.selected_date = t_selected_date;
|
351
|
+
this.selection_made = true;
|
352
|
+
}
|
353
|
+
|
354
|
+
if (!isNaN(parts.get("hour"))) this.selected_date.setHours(parts.get("hour"));
|
355
|
+
if (!isNaN(parts.get("minute"))) this.selected_date.setMinutes( Math.floor_to_interval(parts.get("minute"), this.options.get("minute_interval")) );
|
356
|
+
if (parts.get("hour") === "" || parts.get("minute") === "")
|
357
|
+
this.setUseTime(false);
|
358
|
+
else if (!isNaN(parts.get("hour")) || !isNaN(parts.get("minute")))
|
359
|
+
this.setUseTime(true);
|
360
|
+
|
361
|
+
this.updateFooter();
|
362
|
+
this.setSelectedClass();
|
363
|
+
|
364
|
+
if (this.selection_made) this.updateValue();
|
365
|
+
if (this.closeOnClick()) { this.close(); }
|
366
|
+
if (via_click && !this.options.get("embedded")) {
|
367
|
+
if ((new Date() - this.last_click_at) < 333) this.close();
|
368
|
+
this.last_click_at = new Date();
|
369
|
+
}
|
370
|
+
},
|
371
|
+
closeOnClick: function() {
|
372
|
+
if (this.options.get("embedded")) return false;
|
373
|
+
if (this.options.get("close_on_click")===nil )
|
374
|
+
return (this.options.get("time")) ? false : true
|
375
|
+
else
|
376
|
+
return (this.options.get("close_on_click"))
|
377
|
+
},
|
378
|
+
navMonth: function(month) { (target_date = new Date(this.date)).setMonth(month); return (this.navTo(target_date)); },
|
379
|
+
navYear: function(year) { (target_date = new Date(this.date)).setYear(year); return (this.navTo(target_date)); },
|
380
|
+
navTo: function(date) {
|
381
|
+
if (!this.validYear(date.getFullYear())) return false;
|
382
|
+
this.date = date;
|
383
|
+
this.date.setDate(1);
|
384
|
+
this.refresh();
|
385
|
+
this.callback("after_navigate", this.date);
|
386
|
+
return true;
|
387
|
+
},
|
388
|
+
setUseTime: function(turn_on) {
|
389
|
+
this.use_time = this.options.get("time") && (this.options.get("time")=="mixed" ? turn_on : true) // force use_time to true if time==true && time!="mixed"
|
390
|
+
if (this.use_time && this.selected_date) { // only set hour/minute if a date is already selected
|
391
|
+
var minute = Math.floor_to_interval(this.selected_date.getMinutes(), this.options.get("minute_interval"));
|
392
|
+
var hour = this.selected_date.getHours();
|
393
|
+
|
394
|
+
this.hour_select.setValue(hour);
|
395
|
+
this.minute_select.setValue(minute)
|
396
|
+
} else if (this.options.get("time")=="mixed") {
|
397
|
+
this.hour_select.setValue(""); this.minute_select.setValue("");
|
398
|
+
}
|
399
|
+
},
|
400
|
+
updateValue: function() {
|
401
|
+
var last_value = this.target_element.value;
|
402
|
+
this.target_element.value = this.dateString();
|
403
|
+
if (last_value!=this.target_element.value) this.callback("onchange");
|
404
|
+
},
|
405
|
+
today: function(now) {
|
406
|
+
var d = new Date(); this.date = new Date();
|
407
|
+
var o = $H({ day: d.getDate(), month: d.getMonth(), year: d.getFullYear(), hour: d.getHours(), minute: d.getMinutes()});
|
408
|
+
if ( ! now ) o = o.merge({hour: "", minute:""});
|
409
|
+
this.updateSelectedDate(o, true);
|
410
|
+
this.refresh();
|
411
|
+
},
|
412
|
+
close: function() {
|
413
|
+
if (this.closed) return false;
|
414
|
+
this.callback("before_close");
|
415
|
+
this.target_element.calendar_date_select = nil;
|
416
|
+
Event.stopObserving(document, "mousedown", this.closeIfClickedOut_handler);
|
417
|
+
Event.stopObserving(document, "keypress", this.keyPress_handler);
|
418
|
+
this.calendar_div.remove(); this.closed = true;
|
419
|
+
if (this.iframe) this.iframe.remove();
|
420
|
+
if (this.target_element.type!="hidden") this.target_element.focus();
|
421
|
+
this.callback("after_close");
|
422
|
+
},
|
423
|
+
closeIfClickedOut: function(e) {
|
424
|
+
if (! $(Event.element(e)).descendantOf(this.calendar_div) ) this.close();
|
425
|
+
},
|
426
|
+
keyPress: function(e) {
|
427
|
+
if (e.keyCode==Event.KEY_ESC) this.close();
|
428
|
+
},
|
429
|
+
callback: function(name, param) { if (this.options.get(name)) { this.options.get(name).bind(this.target_element)(param); } }
|
430
|
+
}
|