bhf 0.4.4 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,161 @@
1
+ /*
2
+ ---
3
+ name: Picker.Attach
4
+ description: Adds attach and detach methods to the Picker, to attach it to element events
5
+ authors: Arian Stolwijk
6
+ requires: [Picker, Core/Element.Event]
7
+ provides: Picker.Attach
8
+ ...
9
+ */
10
+
11
+
12
+ Picker.Attach = new Class({
13
+
14
+ Extends: Picker,
15
+
16
+ options: {/*
17
+ onAttached: function(event){},
18
+
19
+ toggleElements: null, // deprecated
20
+ toggle: null, // When set it deactivate toggling by clicking on the input */
21
+ togglesOnly: true, // set to false to always make calendar popup on input element, if true, it depends on the toggles elements set.
22
+ showOnInit: false, // overrides the Picker option
23
+ blockKeydown: true
24
+ },
25
+
26
+ initialize: function(attachTo, options){
27
+ this.parent(options);
28
+
29
+ this.attachedEvents = [];
30
+ this.attachedElements = [];
31
+ this.toggles = [];
32
+ this.inputs = [];
33
+
34
+ var documentEvent = function(event){
35
+ if (this.attachedElements.contains(event.target)) return;
36
+ this.close();
37
+ }.bind(this);
38
+ var document = this.picker.getDocument().addEvent('click', documentEvent);
39
+
40
+ var preventPickerClick = function(event){
41
+ event.stopPropagation();
42
+ return false;
43
+ };
44
+ this.picker.addEvent('click', preventPickerClick);
45
+
46
+ // Support for deprecated toggleElements
47
+ if (this.options.toggleElements) this.options.toggle = document.getElements(this.options.toggleElements);
48
+
49
+ this.attach(attachTo, this.options.toggle);
50
+ },
51
+
52
+ attach: function(attachTo, toggle){
53
+ if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo);
54
+ if (typeOf(toggle) == 'string') toggle = document.id(toggle);
55
+
56
+ var elements = Array.from(attachTo),
57
+ toggles = Array.from(toggle),
58
+ allElements = [].append(elements).combine(toggles),
59
+ self = this;
60
+
61
+ var closeEvent = function(event){
62
+ var stopInput = self.options.blockKeydown
63
+ && event.type == 'keydown'
64
+ && !(['tab', 'esc'].contains(event.key)),
65
+ isCloseKey = event.type == 'keydown'
66
+ && (['tab', 'esc'].contains(event.key)),
67
+ isA = event.target.get('tag') == 'a';
68
+
69
+ if (stopInput || isA) event.preventDefault();
70
+ if (isCloseKey || isA) self.close();
71
+ };
72
+
73
+ var getOpenEvent = function(element){
74
+ return function(event){
75
+ var tag = event.target.get('tag');
76
+ if (tag == 'input' && event.type == 'click' && !element.match(':focus') || (self.opened && self.input == element)) return;
77
+ if (tag == 'a') event.stop();
78
+ self.position(element);
79
+ self.open();
80
+ self.fireEvent('attached', [event, element]);
81
+ };
82
+ };
83
+
84
+ var getToggleEvent = function(open, close){
85
+ return function(event){
86
+ if (self.opened) close(event);
87
+ else open(event);
88
+ };
89
+ };
90
+
91
+ allElements.each(function(element){
92
+
93
+ // The events are already attached!
94
+ if (self.attachedElements.contains(element)) return;
95
+
96
+ var events = {},
97
+ tag = element.get('tag'),
98
+ openEvent = getOpenEvent(element),
99
+ // closeEvent does not have a depency on element
100
+ toggleEvent = getToggleEvent(openEvent, closeEvent);
101
+
102
+ if (tag == 'input'){
103
+ // Fix in order to use togglers only
104
+ if (!self.options.togglesOnly || !toggles.length){
105
+ events = {
106
+ focus: openEvent,
107
+ click: openEvent,
108
+ keydown: closeEvent
109
+ };
110
+ }
111
+ self.inputs.push(element);
112
+ } else {
113
+ if (toggles.contains(element)){
114
+ self.toggles.push(element);
115
+ events.click = toggleEvent
116
+ } else {
117
+ events.click = openEvent;
118
+ }
119
+ }
120
+ element.addEvents(events);
121
+ self.attachedElements.push(element);
122
+ self.attachedEvents.push(events);
123
+ });
124
+ return this;
125
+ },
126
+
127
+ detach: function(attachTo, toggle){
128
+ if (typeOf(attachTo) == 'string') attachTo = document.id(attachTo);
129
+ if (typeOf(toggle) == 'string') toggle = document.id(toggle);
130
+
131
+ var elements = Array.from(attachTo),
132
+ toggles = Array.from(toggle),
133
+ allElements = [].append(elements).combine(toggles),
134
+ self = this;
135
+
136
+ if (!allElements.length) allElements = self.attachedElements;
137
+
138
+ allElements.each(function(element){
139
+ var i = self.attachedElements.indexOf(element);
140
+ if (i < 0) return;
141
+
142
+ var events = self.attachedEvents[i];
143
+ element.removeEvents(events);
144
+ delete self.attachedEvents[i];
145
+ delete self.attachedElements[i];
146
+
147
+ var toggleIndex = self.toggles.indexOf(element);
148
+ if (toggleIndex != -1) delete self.toggles[toggleIndex];
149
+
150
+ var inputIndex = self.inputs.indexOf(element);
151
+ if (toggleIndex != -1) delete self.inputs[inputIndex];
152
+ });
153
+ return this;
154
+ },
155
+
156
+ destroy: function(){
157
+ this.detach();
158
+ return this.parent();
159
+ }
160
+
161
+ });
@@ -0,0 +1,668 @@
1
+ /*
2
+ ---
3
+ name: Picker.Date
4
+ description: Creates a DatePicker, can be used for picking years/months/days and time, or all of them
5
+ authors: Arian Stolwijk
6
+ requires: [Picker, Picker.Attach, Locale.en-US.DatePicker, More/Locale, More/Date]
7
+ provides: Picker.Date
8
+ ...
9
+ */
10
+
11
+
12
+ (function(){
13
+
14
+ this.DatePicker = Picker.Date = new Class({
15
+
16
+ Extends: Picker.Attach,
17
+
18
+ options: {/*
19
+ onSelect: function(date){},
20
+
21
+ minDate: new Date('3/4/2010'), // Date object or a string
22
+ maxDate: new Date('3/4/2011'), // same as minDate
23
+ availableDates: {}, //
24
+ invertAvailable: false,
25
+
26
+ format: null,*/
27
+
28
+ timePicker: false,
29
+ timePickerOnly: false, // deprecated, use onlyView = 'time'
30
+ timeWheelStep: 1, // 10,15,20,30
31
+
32
+ yearPicker: true,
33
+ yearsPerPage: 20,
34
+
35
+ startDay: 1, // Sunday (0) through Saturday (6) - be aware that this may affect your layout, since the days on the right might have a different margin
36
+ rtl: false,
37
+
38
+ startView: 'days', // allowed values: {time, days, months, years}
39
+ openLastView: false,
40
+ pickOnly: false, // 'years', 'months', 'days', 'time'
41
+ canAlwaysGoUp: ['months', 'days'],
42
+ updateAll : false, //whether or not to update all inputs when selecting a date
43
+
44
+ weeknumbers: false,
45
+
46
+ // if you like to use your own translations
47
+ months_abbr: null,
48
+ days_abbr: null,
49
+ years_title: function(date, options){
50
+ var year = date.get('year');
51
+ return year + '-' + (year + options.yearsPerPage - 1);
52
+ },
53
+ months_title: function(date, options){
54
+ return date.get('year');
55
+ },
56
+ days_title: function(date, options){
57
+ return date.format('%b %Y');
58
+ },
59
+ time_title: function(date, options){
60
+ return (options.pickOnly == 'time') ? Locale.get('DatePicker.select_a_time') : date.format('%d %B, %Y');
61
+ }
62
+ },
63
+
64
+ initialize: function(attachTo, options){
65
+ this.parent(attachTo, options);
66
+
67
+ this.setOptions(options);
68
+ options = this.options;
69
+
70
+ // If we only want to use one picker / backwards compatibility
71
+ ['year', 'month', 'day', 'time'].some(function(what){
72
+ if (options[what + 'PickerOnly']){
73
+ options.pickOnly = what;
74
+ return true;
75
+ }
76
+ return false;
77
+ });
78
+ if (options.pickOnly){
79
+ options[options.pickOnly + 'Picker'] = true;
80
+ options.startView = options.pickOnly;
81
+ }
82
+
83
+ // backward compatibility for startView
84
+ var newViews = ['days', 'months', 'years'];
85
+ ['month', 'year', 'decades'].some(function(what, i){
86
+ return (options.startView == what) && (options.startView = newViews[i]);
87
+ });
88
+
89
+ options.canAlwaysGoUp = options.canAlwaysGoUp ? Array.from(options.canAlwaysGoUp) : [];
90
+
91
+ // Set the min and max dates as Date objects
92
+ if (options.minDate){
93
+ if (!(options.minDate instanceof Date)) options.minDate = Date.parse(options.minDate);
94
+ options.minDate.clearTime();
95
+ }
96
+ if (options.maxDate){
97
+ if (!(options.maxDate instanceof Date)) options.maxDate = Date.parse(options.maxDate);
98
+ options.maxDate.clearTime();
99
+ }
100
+
101
+ if (!options.format){
102
+ options.format = (options.pickOnly != 'time') ? Locale.get('Date.shortDate') : '';
103
+ if (options.timePicker) options.format = (options.format) + (options.format ? ' ' : '') + Locale.get('Date.shortTime');
104
+ }
105
+
106
+ // Some link or input has fired an event!
107
+ this.addEvent('attached', function(event, element){
108
+
109
+ // This is where we store the selected date
110
+ if (!this.currentView || !options.openLastView) this.currentView = options.startView;
111
+
112
+ this.date = limitDate(new Date(), options.minDate, options.maxDate);
113
+ var tag = element.get('tag'), input;
114
+ if (tag == 'input') input = element;
115
+ else {
116
+ var index = this.toggles.indexOf(element);
117
+ if (this.inputs[index]) input = this.inputs[index];
118
+ }
119
+ this.getInputDate(input);
120
+ this.input = input;
121
+ this.setColumns(this.originalColumns);
122
+ }.bind(this), true);
123
+
124
+ },
125
+
126
+ getInputDate: function(input){
127
+ this.date = new Date();
128
+ if (!input) return;
129
+ var date = Date.parse(input.get('value'));
130
+ if (!date || !date.isValid()){
131
+ var storeDate = input.retrieve('datepicker:value');
132
+ if (storeDate) date = Date.parse(storeDate);
133
+ }
134
+ if (date && date != null && date.isValid()) this.date = date;
135
+ },
136
+
137
+ // Control the previous and next elements
138
+
139
+ constructPicker: function(){
140
+ this.parent();
141
+
142
+ if (!this.options.rtl){
143
+ this.previous = new Element('div.previous[html=&#171;]').inject(this.header);
144
+ this.next = new Element('div.next[html=&#187;]').inject(this.header);
145
+ } else {
146
+ this.next = new Element('div.previous[html=&#171;]').inject(this.header);
147
+ this.previous = new Element('div.next[html=&#187;]').inject(this.header);
148
+ }
149
+ },
150
+
151
+ hidePrevious: function(_next, _show){
152
+ this[_next ? 'next' : 'previous'].setStyle('display', _show ? 'block' : 'none');
153
+ return this;
154
+ },
155
+
156
+ showPrevious: function(_next){
157
+ return this.hidePrevious(_next, true);
158
+ },
159
+
160
+ setPreviousEvent: function(fn, _next){
161
+ this[_next ? 'next' : 'previous'].removeEvents('click');
162
+ if (fn) this[_next ? 'next' : 'previous'].addEvent('click', fn);
163
+ return this;
164
+ },
165
+
166
+ hideNext: function(){
167
+ return this.hidePrevious(true);
168
+ },
169
+
170
+ showNext: function(){
171
+ return this.showPrevious(true);
172
+ },
173
+
174
+ setNextEvent: function(fn){
175
+ return this.setPreviousEvent(fn, true);
176
+ },
177
+
178
+ setColumns: function(columns, view, date, viewFx){
179
+ var ret = this.parent(columns), method;
180
+
181
+ if ((view || this.currentView)
182
+ && (method = 'render' + (view || this.currentView).capitalize())
183
+ && this[method]
184
+ ) this[method](date || this.date.clone(), viewFx);
185
+
186
+ return ret;
187
+ },
188
+
189
+ // Render the Pickers
190
+
191
+ renderYears: function(date, fx){
192
+ var options = this.options, pages = options.columns, perPage = options.yearsPerPage,
193
+ _columns = [], _dates = [];
194
+ this.dateElements = [];
195
+
196
+ // start neatly at interval (eg. 1980 instead of 1987)
197
+ date = date.clone().decrement('year', date.get('year') % perPage);
198
+
199
+ var iterateDate = date.clone().decrement('year', Math.floor((pages - 1) / 2) * perPage);
200
+
201
+ for (var i = pages; i--;){
202
+ var _date = iterateDate.clone();
203
+ _dates.push(_date);
204
+ _columns.push(renderers.years(
205
+ timesSelectors.years(options, _date.clone()),
206
+ options,
207
+ this.date.clone(),
208
+ this.dateElements,
209
+ function(date){
210
+ if (options.pickOnly == 'years') this.select(date);
211
+ else this.renderMonths(date, 'fade');
212
+ this.date = date;
213
+ }.bind(this)
214
+ ));
215
+ iterateDate.increment('year', perPage);
216
+ }
217
+
218
+ this.setColumnsContent(_columns, fx);
219
+ this.setTitle(_dates, options.years_title);
220
+
221
+ // Set limits
222
+ var limitLeft = (options.minDate && date.get('year') <= options.minDate.get('year')),
223
+ limitRight = (options.maxDate && (date.get('year') + options.yearsPerPage) >= options.maxDate.get('year'));
224
+ this[(limitLeft ? 'hide' : 'show') + 'Previous']();
225
+ this[(limitRight ? 'hide' : 'show') + 'Next']();
226
+
227
+ this.setPreviousEvent(function(){
228
+ this.renderYears(date.decrement('year', perPage), 'left');
229
+ }.bind(this));
230
+
231
+ this.setNextEvent(function(){
232
+ this.renderYears(date.increment('year', perPage), 'right');
233
+ }.bind(this));
234
+
235
+ // We can't go up!
236
+ this.setTitleEvent(null);
237
+
238
+ this.currentView = 'years';
239
+ },
240
+
241
+ renderMonths: function(date, fx){
242
+ var options = this.options, years = options.columns, _columns = [], _dates = [],
243
+ iterateDate = date.clone().decrement('year', Math.floor((years - 1) / 2));
244
+ this.dateElements = [];
245
+
246
+ for (var i = years; i--;){
247
+ var _date = iterateDate.clone();
248
+ _dates.push(_date);
249
+ _columns.push(renderers.months(
250
+ timesSelectors.months(options, _date.clone()),
251
+ options,
252
+ this.date.clone(),
253
+ this.dateElements,
254
+ function(date){
255
+ if (options.pickOnly == 'months') this.select(date);
256
+ else this.renderDays(date, 'fade');
257
+ this.date = date;
258
+ }.bind(this)
259
+ ));
260
+ iterateDate.increment('year', 1);
261
+ }
262
+
263
+ this.setColumnsContent(_columns, fx);
264
+ this.setTitle(_dates, options.months_title);
265
+
266
+ // Set limits
267
+ var year = date.get('year'),
268
+ limitLeft = (options.minDate && year <= options.minDate.get('year')),
269
+ limitRight = (options.maxDate && year >= options.maxDate.get('year'));
270
+ this[(limitLeft ? 'hide' : 'show') + 'Previous']();
271
+ this[(limitRight ? 'hide' : 'show') + 'Next']();
272
+
273
+ this.setPreviousEvent(function(){
274
+ this.renderMonths(date.decrement('year', years), 'left');
275
+ }.bind(this));
276
+
277
+ this.setNextEvent(function(){
278
+ this.renderMonths(date.increment('year', years), 'right');
279
+ }.bind(this));
280
+
281
+ var canGoUp = options.yearPicker && (options.pickOnly != 'months' || options.canAlwaysGoUp.contains('months'));
282
+ var titleEvent = (canGoUp) ? function(){
283
+ this.renderYears(date, 'fade');
284
+ }.bind(this) : null;
285
+ this.setTitleEvent(titleEvent);
286
+
287
+ this.currentView = 'months';
288
+ },
289
+
290
+ renderDays: function(date, fx){
291
+ var options = this.options, months = options.columns, _columns = [], _dates = [],
292
+ iterateDate = date.clone().decrement('month', Math.floor((months - 1) / 2));
293
+ this.dateElements = [];
294
+
295
+ for (var i = months; i--;){
296
+ _date = iterateDate.clone();
297
+ _dates.push(_date);
298
+ _columns.push(renderers.days(
299
+ timesSelectors.days(options, _date.clone()),
300
+ options,
301
+ this.date.clone(),
302
+ this.dateElements,
303
+ function(date){
304
+ if (options.pickOnly == 'days' || !options.timePicker) this.select(date)
305
+ else this.renderTime(date, 'fade');
306
+ this.date = date;
307
+ }.bind(this)
308
+ ));
309
+ iterateDate.increment('month', 1);
310
+ }
311
+
312
+ this.setColumnsContent(_columns, fx);
313
+ this.setTitle(_dates, options.days_title);
314
+
315
+ var yearmonth = date.format('%Y%m').toInt(),
316
+ limitLeft = (options.minDate && yearmonth <= options.minDate.format('%Y%m')),
317
+ limitRight = (options.maxDate && yearmonth >= options.maxDate.format('%Y%m'));
318
+ this[(limitLeft ? 'hide' : 'show') + 'Previous']();
319
+ this[(limitRight ? 'hide' : 'show') + 'Next']();
320
+
321
+ this.setPreviousEvent(function(){
322
+ this.renderDays(date.decrement('month', months), 'left');
323
+ }.bind(this));
324
+
325
+ this.setNextEvent(function(){
326
+ this.renderDays(date.increment('month', months), 'right');
327
+ }.bind(this));
328
+
329
+ var canGoUp = options.pickOnly != 'days' || options.canAlwaysGoUp.contains('days');
330
+ var titleEvent = (canGoUp) ? function(){
331
+ this.renderMonths(date, 'fade');
332
+ }.bind(this) : null;
333
+ this.setTitleEvent(titleEvent);
334
+
335
+ this.currentView = 'days';
336
+ },
337
+
338
+ renderTime: function(date, fx){
339
+ var options = this.options;
340
+ this.setTitle(date, options.time_title);
341
+
342
+ var originalColumns = this.originalColumns = options.columns;
343
+ this.currentView = null; // otherwise you'd get crazy recursion
344
+ if (originalColumns != 1) this.setColumns(1);
345
+
346
+ this.setContent(renderers.time(
347
+ options,
348
+ date.clone(),
349
+ function(date){
350
+ this.select(date);
351
+ }.bind(this)
352
+ ), fx);
353
+
354
+ // Hide « and » buttons
355
+ this.hidePrevious()
356
+ .hideNext()
357
+ .setPreviousEvent(null)
358
+ .setNextEvent(null);
359
+
360
+ var canGoUp = options.pickOnly != 'time' || options.canAlwaysGoUp.contains('time');
361
+ var titleEvent = (canGoUp) ? function(){
362
+ this.setColumns(originalColumns, 'days', date, 'fade');
363
+ }.bind(this) : null;
364
+ this.setTitleEvent(titleEvent);
365
+
366
+ this.currentView = 'time';
367
+ },
368
+
369
+ select: function(date, all){
370
+ this.date = date;
371
+ var formatted = date.format(this.options.format),
372
+ time = date.strftime(),
373
+ inputs = (!this.options.updateAll && !all && this.input) ? [this.input] : this.inputs;
374
+
375
+ inputs.each(function(input){
376
+ input.set('value', formatted).store('datepicker:value', time).fireEvent('change');
377
+ }, this);
378
+
379
+ this.fireEvent('select', [date].concat(inputs));
380
+ this.close();
381
+ return this;
382
+ }
383
+
384
+ });
385
+
386
+
387
+ // Renderers only output elements and calculate the limits!
388
+
389
+ var timesSelectors = {
390
+
391
+ years: function(options, date){
392
+ var times = [];
393
+ for (var i = 0; i < options.yearsPerPage; i++){
394
+ times.push(+date);
395
+ date.increment('year', 1);
396
+ }
397
+ return times;
398
+ },
399
+
400
+ months: function(options, date){
401
+ var times = [];
402
+ date.set('month', 0);
403
+ for (var i = 0; i <= 11; i++){
404
+ times.push(+date);
405
+ date.increment('month', 1);
406
+ }
407
+ return times;
408
+ },
409
+
410
+ days: function(options, date){
411
+ var times = [];
412
+ date.set('date', 1);
413
+ while (date.get('day') != options.startDay) date.set('date', date.get('date') - 1);
414
+ for (var i = 0; i < 42; i++){
415
+ times.push(+date);
416
+ date.increment('day', 1);
417
+ }
418
+ return times;
419
+ }
420
+
421
+ };
422
+
423
+ var renderers = {
424
+
425
+ years: function(years, options, currentDate, dateElements, fn){
426
+ var container = new Element('div.years'),
427
+ today = new Date(), element, classes;
428
+
429
+ years.each(function(_year, i){
430
+ var date = new Date(_year), year = date.get('year');
431
+
432
+ classes = '.year.year' + i;
433
+ if (year == today.get('year')) classes += '.today';
434
+ if (year == currentDate.get('year')) classes += '.selected';
435
+ element = new Element('div' + classes, {text: year}).inject(container);
436
+
437
+ dateElements.push({element: element, time: _year});
438
+
439
+ if (isUnavailable('year', date, options)) element.addClass('unavailable');
440
+ else element.addEvent('click', fn.pass(date));
441
+ });
442
+
443
+ return container;
444
+ },
445
+
446
+ months: function(months, options, currentDate, dateElements, fn){
447
+ var today = new Date(),
448
+ month = today.get('month'),
449
+ thisyear = today.get('year'),
450
+ selectedyear = currentDate.get('year'),
451
+ container = new Element('div.months'),
452
+ monthsAbbr = options.months_abbr || Locale.get('Date.months_abbr'),
453
+ element, classes;
454
+
455
+ months.each(function(_month, i){
456
+ var date = new Date(_month), year = date.get('year');
457
+
458
+ classes = '.month.month' + (i + 1);
459
+ if (i == month && year == thisyear) classes += '.today';
460
+ if (i == currentDate.get('month') && year == selectedyear) classes += '.selected';
461
+ element = new Element('div' + classes, {text: monthsAbbr[i]}).inject(container);
462
+
463
+ dateElements.push({element: element, time: _month});
464
+
465
+ if (isUnavailable('month', date, options)) element.addClass('unavailable');
466
+ else element.addEvent('click', fn.pass(date));
467
+ });
468
+
469
+ return container;
470
+ },
471
+
472
+ days: function(days, options, currentDate, dateElements, fn){
473
+ var month = new Date(days[14]).get('month'),
474
+ todayString = new Date().toDateString(),
475
+ currentString = currentDate.toDateString(),
476
+ weeknumbers = options.weeknumbers,
477
+ container = new Element('table.days' + (weeknumbers ? '.weeknumbers' : ''), {
478
+ role: 'grid', 'aria-labelledby': this.titleID
479
+ }),
480
+ header = new Element('thead').inject(container),
481
+ body = new Element('tbody').inject(container),
482
+ titles = new Element('tr.titles').inject(header),
483
+ localeDaysShort = options.days_abbr || Locale.get('Date.days_abbr'),
484
+ day, classes, element, weekcontainer, dateString,
485
+ where = options.rtl ? 'top' : 'bottom';
486
+
487
+ if (weeknumbers) new Element('th.title.day.weeknumber', {
488
+ text: Locale.get('DatePicker.week')
489
+ }).inject(titles);
490
+
491
+ for (day = options.startDay; day < (options.startDay + 7); day++){
492
+ new Element('th.title.day.day' + (day % 7), {
493
+ text: localeDaysShort[(day % 7)],
494
+ role: 'columnheader'
495
+ }).inject(titles, where);
496
+ }
497
+
498
+ days.each(function(_date, i){
499
+ var date = new Date(_date);
500
+
501
+ if (i % 7 == 0){
502
+ weekcontainer = new Element('tr.week.week' + (Math.floor(i / 7))).set('role', 'row').inject(body);
503
+ if (weeknumbers) new Element('th.day.weeknumber', {text: date.get('week'), scope: 'row', role: 'rowheader'}).inject(weekcontainer);
504
+ }
505
+
506
+ dateString = date.toDateString();
507
+ classes = '.day.day' + date.get('day');
508
+ if (dateString == todayString) classes += '.today';
509
+ if (date.get('month') != month) classes += '.otherMonth';
510
+ element = new Element('td' + classes, {text: date.getDate(), role: 'gridcell'}).inject(weekcontainer, where);
511
+
512
+ if (dateString == currentString) element.addClass('selected').set('aria-selected', 'true');
513
+ else element.set('aria-selected', 'false');
514
+
515
+ dateElements.push({element: element, time: _date});
516
+
517
+ if (isUnavailable('date', date, options)) element.addClass('unavailable');
518
+ else element.addEvent('click', fn.pass(date.clone()));
519
+ });
520
+
521
+ return container;
522
+ },
523
+
524
+ time: function(options, date, fn){
525
+ var container = new Element('div.time'),
526
+ // make sure that the minutes are timeWheelStep * k
527
+ initMinutes = (date.get('minutes') / options.timeWheelStep).round() * options.timeWheelStep
528
+
529
+ if (initMinutes >= 60) initMinutes = 0;
530
+ date.set('minutes', initMinutes);
531
+
532
+ var hoursInput = new Element('input.hour[type=text]', {
533
+ title: Locale.get('DatePicker.use_mouse_wheel'),
534
+ value: date.format('%H'),
535
+ events: {
536
+ click: function(event){
537
+ event.target.focus();
538
+ event.stop();
539
+ },
540
+ mousewheel: function(event){
541
+ event.stop();
542
+ hoursInput.focus();
543
+ var value = hoursInput.get('value').toInt();
544
+ value = (event.wheel > 0) ? ((value < 23) ? value + 1 : 0)
545
+ : ((value > 0) ? value - 1 : 23)
546
+ date.set('hours', value);
547
+ hoursInput.set('value', date.format('%H'));
548
+ }.bind(this)
549
+ },
550
+ maxlength: 2
551
+ }).inject(container);
552
+
553
+ var minutesInput = new Element('input.minutes[type=text]', {
554
+ title: Locale.get('DatePicker.use_mouse_wheel'),
555
+ value: date.format('%M'),
556
+ events: {
557
+ click: function(event){
558
+ event.target.focus();
559
+ event.stop();
560
+ },
561
+ mousewheel: function(event){
562
+ event.stop();
563
+ minutesInput.focus();
564
+ var value = minutesInput.get('value').toInt();
565
+ value = (event.wheel > 0) ? ((value < 59) ? (value + options.timeWheelStep) : 0)
566
+ : ((value > 0) ? (value - options.timeWheelStep) : (60 - options.timeWheelStep));
567
+ if (value >= 60) value = 0;
568
+ date.set('minutes', value);
569
+ minutesInput.set('value', date.format('%M'));
570
+ }.bind(this)
571
+ },
572
+ maxlength: 2
573
+ }).inject(container);
574
+
575
+ new Element('div.separator[text=:]').inject(container);
576
+
577
+ new Element('input.ok[type=submit]', {
578
+ value: Locale.get('DatePicker.time_confirm_button'),
579
+ events: {click: function(event){
580
+ event.stop();
581
+ date.set({
582
+ hours: hoursInput.get('value').toInt(),
583
+ minutes: minutesInput.get('value').toInt()
584
+ });
585
+ fn(date.clone());
586
+ }}
587
+ }).inject(container);
588
+
589
+ return container;
590
+ }
591
+
592
+ };
593
+
594
+
595
+ Picker.Date.defineRenderer = function(name, fn){
596
+ renderers[name] = fn;
597
+ return this;
598
+ };
599
+
600
+ var limitDate = function(date, min, max){
601
+ if (min && date < min) return min;
602
+ if (max && date > max) return max;
603
+ return date;
604
+ };
605
+
606
+ var isUnavailable = function(type, date, options){
607
+ var minDate = options.minDate,
608
+ maxDate = options.maxDate,
609
+ availableDates = options.availableDates,
610
+ year, month, day, ms;
611
+
612
+ if (!minDate && !maxDate && !availableDates) return false;
613
+ date.clearTime();
614
+
615
+ if (type == 'year'){
616
+ year = date.get('year');
617
+ return (
618
+ (minDate && year < minDate.get('year')) ||
619
+ (maxDate && year > maxDate.get('year')) ||
620
+ (
621
+ (availableDates != null && !options.invertAvailable) && (
622
+ availableDates[year] == null ||
623
+ Object.getLength(availableDates[year]) == 0 ||
624
+ Object.getLength(
625
+ Object.filter(availableDates[year], function(days){
626
+ return (days.length > 0);
627
+ })
628
+ ) == 0
629
+ )
630
+ )
631
+ );
632
+ }
633
+
634
+ if (type == 'month'){
635
+ year = date.get('year');
636
+ month = date.get('month') + 1;
637
+ ms = date.format('%Y%m').toInt();
638
+ return (
639
+ (minDate && ms < minDate.format('%Y%m').toInt()) ||
640
+ (maxDate && ms > maxDate.format('%Y%m').toInt()) ||
641
+ (
642
+ (availableDates != null && !options.invertAvailable) && (
643
+ availableDates[year] == null ||
644
+ availableDates[year][month] == null ||
645
+ availableDates[year][month].length == 0
646
+ )
647
+ )
648
+ );
649
+ }
650
+
651
+ // type == 'date'
652
+ year = date.get('year');
653
+ month = date.get('month') + 1;
654
+ day = date.get('date');
655
+
656
+ var dateAllow = (minDate && date < minDate) || (maxDate && date > maxDate);
657
+ if (availableDates != null){
658
+ dateAllow = dateAllow
659
+ || availableDates[year] == null
660
+ || availableDates[year][month] == null
661
+ || !availableDates[year][month].contains(day);
662
+ if (options.invertAvailable) dateAllow = !dateAllow;
663
+ }
664
+
665
+ return dateAllow;
666
+ };
667
+
668
+ })();