DateSelector = Behavior.create({
initialize: function(options) {
this.element.addClassName('date_selector');
this.calendar = null;
this.options = Object.extend(DateSelector.DEFAULTS, options || {});
this.date = this.getDate();
this._createCalendar();
},
setDate : function(value) {
this.date = value;
this.element.value = this.options.setter(this.date);
if (this.calendar)
setTimeout(this.calendar.element.hide.bind(this.calendar.element), 500);
},
_createCalendar : function() {
var calendar = $div({ 'class' : 'date_selector' });
document.body.appendChild(calendar);
calendar.setStyle({
position : 'absolute',
zIndex : '500',
top : Position.cumulativeOffset(this.element)[1] + this.element.getHeight() + 'px',
left : Position.cumulativeOffset(this.element)[0] + 'px'
});
this.calendar = new Calendar(calendar, this);
},
onclick : function(e) {
this.calendar.show();
Event.stop(e);
},
onfocus : function(e) {
this.onclick(e);
},
getDate : function() {
return this.options.getter(this.element.value) || new Date;
}
});
Calendar = Behavior.create({
initialize : function(selector) {
this.selector = selector;
this.element.hide();
Event.observe(document, 'click', this.element.hide.bind(this.element));
},
show : function() {
Calendar.instances.invoke('hide');
this.date = this.selector.getDate();
this.redraw();
this.element.show();
this.active = true;
},
hide : function() {
this.element.hide();
this.active = false;
},
redraw : function() {
var html = '
' +
' ' +
' | ← | ' +
' ' + this._label() + ' | ' +
' → |
' +
' ' +
' ' +
' ';
html += this._buildDateCells();
html += '
';
this.element.innerHTML = html;
},
onclick : function(e) {
var source = Event.element(e);
Event.stop(e);
if ($(source.parentNode).hasClassName('day')) return this._setDate(source);
if ($(source.parentNode).hasClassName('back')) return this._backMonth();
if ($(source.parentNode).hasClassName('forward')) return this._forwardMonth();
},
_setDate : function(source) {
if (source.innerHTML.strip() != '') {
this.date.setDate(parseInt(source.innerHTML));
this.selector.setDate(this.date);
this.element.getElementsByClassName('selected').invoke('removeClassName', 'selected');
source.parentNode.addClassName('selected');
}
},
_backMonth : function() {
this.date.setMonth(this.date.getMonth() - 1);
this.redraw();
return false;
},
_forwardMonth : function() {
this.date.setMonth(this.date.getMonth() + 1);
this.redraw();
return false;
},
_getDateFromSelector : function() {
this.date = new Date(this.selector.date.getTime());
},
_firstDay : function(month, year) {
return new Date(year, month, 1).getDay();
},
_monthLength : function(month, year) {
var length = Calendar.MONTHS[month].days;
return (month == 1 && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))) ? 29 : length;
},
_label : function() {
return Calendar.MONTHS[this.date.getMonth()].label + ' ' + this.date.getFullYear();
},
_dayRows : function() {
for (var i = 0, html='', day; day = Calendar.DAYS[i]; i++)
html += '' + day + ' | ';
return html;
},
_buildDateCells : function() {
var month = this.date.getMonth(), year = this.date.getFullYear();
var day = 1, monthLength = this._monthLength(month, year), firstDay = this._firstDay(month, year);
for (var i = 0, html = ''; i < 9; i++) {
for (var j = 0; j <= 6; j++) {
if (day <= monthLength && (i > 0 || j >= firstDay)) {
var classes = ['day'];
if (this._compareDate(new Date, year, month, day)) classes.push('today');
if (this._compareDate(this.selector.date, year, month, day)) classes.push('selected');
html += '| ' +
'' + day++ + '' +
' | ';
} else html += ' | ';
}
if (day > monthLength) break;
else html += '
';
}
return html + '
';
},
_compareDate : function(date, year, month, day) {
return date.getFullYear() == year &&
date.getMonth() == month &&
date.getDate() == day;
}
});
DateSelector.DEFAULTS = {
setter: function(date) {
return [
date.getFullYear(),
date.getMonth() + 1,
date.getDate()
].join('/');
},
getter: function(value) {
var parsed = Date.parse(value);
if (!isNaN(parsed)) return new Date(parsed);
else return null;
}
}
Object.extend(Calendar, {
DAYS : $w('S M T W T F S'),
MONTHS : [
{ label : 'January', days : 31 },
{ label : 'February', days : 28 },
{ label : 'March', days : 31 },
{ label : 'April', days : 30 },
{ label : 'May', days : 31 },
{ label : 'June', days : 30 },
{ label : 'July', days : 31 },
{ label : 'August', days : 31 },
{ label : 'September', days : 30 },
{ label : 'October', days : 31 },
{ label : 'November', days : 30 },
{ label : 'December', days : 31 }
]
});