401 lines
16 KiB
JavaScript
401 lines
16 KiB
JavaScript
/*
|
|
Month and Year picker for jQuery UI Datepicker 1.8.21
|
|
|
|
Written by Anton Ludescher (silverskater{at}gmail.com).
|
|
Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
|
MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. */
|
|
|
|
if (datepicker == undefined){
|
|
var datepicker_fn = $.fn.datepicker
|
|
var datepicker = $.datepicker
|
|
}
|
|
(function($, undefined ) {
|
|
|
|
//overriding functions meant to be private (starting with an underscore)
|
|
$.ui_datepicker = datepicker
|
|
$.fn.ui_datepicker = datepicker_fn
|
|
$.ui_datepicker._updateDatepicker_MonthYearPicker = $.ui_datepicker._updateDatepicker;
|
|
$.ui_datepicker._showDatepicker_MonthYearPicker = $.ui_datepicker._showDatepicker;
|
|
$.ui_datepicker._doKeyDown_MonthYearPicker = $.ui_datepicker._doKeyDown;
|
|
|
|
$.extend($.ui_datepicker, {
|
|
_doKeyDown: function(event) {
|
|
var inst = $.ui_datepicker._getInst(event.target);
|
|
var handled = true;
|
|
//var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
|
|
inst._keyEvent = true;
|
|
if ($.ui_datepicker._datepickerShowing) {
|
|
switch (event.keyCode) {
|
|
case 27:
|
|
if($('.ui-datepicker-select-month').is(':visible')) {
|
|
$.ui_datepicker._updateDatepicker(inst);
|
|
}
|
|
else if($('.ui-datepicker-select-year').is(':visible')) {
|
|
$.ui_datepicker._toggleDisplay_MonthYearPicker(inst, 2, this);
|
|
}
|
|
else {
|
|
$.ui_datepicker._hideDatepicker();
|
|
}
|
|
break; // hide on escape
|
|
//TODO prev/next month/year on month/year picker screens
|
|
default:
|
|
//call the original function
|
|
$.ui_datepicker._doKeyDown_MonthYearPicker(event);
|
|
}
|
|
}
|
|
else {
|
|
//call the original function
|
|
$.ui_datepicker._doKeyDown_MonthYearPicker(event);
|
|
}
|
|
},
|
|
|
|
_updateDatepicker: function(inst) {
|
|
//call the original function
|
|
if (this._get(inst, 'show_view') == 'year' && !$('.ui-datepicker-select-year').is(':visible')){
|
|
var current_format = this._get(inst, 'dateFormat')
|
|
var regex = /\W/, indice = 0;
|
|
var result = current_format.split(/\W/)
|
|
for (var i=0;i<result.length;i++)
|
|
{
|
|
if (result[i] != '' && result[i].toLowerCase().replace(/y/g,'')==''){
|
|
indice = i
|
|
break
|
|
}
|
|
}
|
|
var current_year = inst.input.val().split(/\W/)[indice];
|
|
if (current_year != '' && current_year != undefined){
|
|
inst.selectedYear = parseInt(current_year)
|
|
inst.drawYear = parseInt(current_year)
|
|
}
|
|
}
|
|
else if (this._get(inst, 'show_view') == 'month' && !$('.ui-datepicker-select-month').is(':visible')){
|
|
var current_format = this._get(inst, 'dateFormat')
|
|
var regex = /\W/, indice_y = 0,indice_m = 0;
|
|
var result = current_format.split(/\W/)
|
|
for (var i=0;i<result.length;i++)
|
|
{
|
|
if (result[i] !== '' && result[i].toLowerCase().replace(/y/g,'')===''){
|
|
indice_y = i
|
|
}
|
|
else if (result[i] !== '' && result[i].toLowerCase().replace(/m/g,'')===''){
|
|
indice_m = i
|
|
}
|
|
}
|
|
var current_array = inst.input.val().split(/\W/)
|
|
var current_year = current_array[indice_y];
|
|
var current_month = current_array[indice_m];
|
|
if (current_year != '' && current_year !== undefined){
|
|
inst.selectedYear = parseInt(current_year)
|
|
inst.drawYear = parseInt(current_year)
|
|
}
|
|
if (current_month != '' && current_month !== undefined){
|
|
inst.selectedMonth = parseInt(current_month) - 1
|
|
inst.drawMonth = parseInt(current_month) - 1
|
|
}
|
|
}
|
|
this._updateDatepicker_MonthYearPicker(inst);
|
|
//TODO: multiMonth
|
|
var numMonths = this._getNumberOfMonths(inst);
|
|
var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
|
|
var changeMonth = this._get(inst, 'changeMonth');
|
|
var changeYear = this._get(inst, 'changeYear');
|
|
if(isMultiMonth || changeMonth || changeYear ) {
|
|
return ;
|
|
}
|
|
|
|
//console.log($('<div>').append(this.dpDiv.clone()).html());
|
|
|
|
var uidptitle = inst.dpDiv.find('.ui-datepicker-title');
|
|
var uidptitle_link = uidptitle.wrapInner('<a href="#"/>');
|
|
uidptitle_link.click(function(){$.ui_datepicker.select_month = false;$.ui_datepicker._toggleDisplay_MonthYearPicker('#' + inst.id, 2); return false;});
|
|
inst.dpDiv.children('table.ui-datepicker-calendar').after(this._generateExtraHTML_MonthYearPicker(inst));
|
|
if (!$('.ui-datepicker-select-year').is(':visible') && !$('.ui-datepicker-select-month').is(':visible')){
|
|
if (this._get(inst, 'show_view') == 'month')
|
|
{
|
|
this._toggleDisplay_MonthYearPicker('#' + inst.id,2)
|
|
}else if (this._get(inst, 'show_view') == 'year'){
|
|
this._toggleDisplay_MonthYearPicker('#' + inst.id,3)
|
|
}
|
|
}
|
|
this._reposition_MonthYearPicker(inst);
|
|
},
|
|
|
|
//focus the date input field
|
|
_instInputFocus_MYP: function(inst) {
|
|
//code copied from datePicker's _updateDatepicker()
|
|
if (inst == $.ui_datepicker._curInst && $.ui_datepicker._datepickerShowing && inst.input &&
|
|
// #6694 - don't focus the input if it's already focused
|
|
// this breaks the change event in IE
|
|
inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
|
|
inst.input.focus();
|
|
|
|
},
|
|
|
|
_generateMonthPickerHTML_MonthYearPicker: function(inst, minDate, maxDate, drawMonth, inMinYear, inMaxYear) {
|
|
//TODO RTL?
|
|
var monthNamesShort = this._get(inst, 'monthNamesShort');
|
|
|
|
var monthPicker = '<table><tbody><tr>';
|
|
|
|
var unselectable = false;
|
|
for (var month = 0; month < 12; ) {
|
|
unselectable = (inMinYear && month < minDate.getMonth()) ||
|
|
(inMaxYear && month > maxDate.getMonth());
|
|
monthPicker += '<td class="' +
|
|
(unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable months
|
|
(month == drawMonth ? ' ui-datepicker-today' : '') + '"' +
|
|
(unselectable ? '' : ' onclick="$.ui_datepicker.select_month = true;jQuery.ui_datepicker._pickMonthYear_MonthYearPicker(\'#' + inst.id + '\', ' + month + ', \'M\');return false;"') + '>' + // actions
|
|
((unselectable ? '<span class="ui-state-default">' + monthNamesShort[month] + '</span>' : '<a class="ui-state-default ' +
|
|
//(month == drawMonth ? ' ui-state-highlight' : '') +
|
|
(month == drawMonth ? ' ui-state-active' : '') + // highlight selected day
|
|
//(otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
|
|
'" href="#">' + monthNamesShort[month] + '</a>')) + '</td>'; // display selectable date
|
|
|
|
if(++month % 4 === 0) {
|
|
monthPicker += '</tr>';
|
|
if(month != 12) {
|
|
monthPicker += '<tr>';
|
|
}
|
|
}
|
|
}
|
|
monthPicker += '</tbody></table>';
|
|
|
|
return monthPicker;
|
|
},
|
|
|
|
_generateExtraHTML_MonthYearPicker: function(inst,show_view) {
|
|
if (show_view != 'year'){
|
|
var minDate = this._getMinMaxDate(inst, 'min');
|
|
var maxDate = this._getMinMaxDate(inst, 'max');
|
|
var drawYear = inst.drawYear;
|
|
var drawMonth = inst.drawMonth;
|
|
var inMinYear = (minDate && minDate.getFullYear() == drawYear);
|
|
var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
|
|
var monthPicker = this._generateMonthPickerHTML_MonthYearPicker(inst, minDate, maxDate, drawMonth, inMinYear, inMaxYear);
|
|
if (show_view != 'month'){
|
|
return '<div class="ui-datepicker-select-month" style="display: none">' + monthPicker + '</div>' +
|
|
'<div class="ui-datepicker-select-year" style="display: none"></div>'; //yearPicker gets filled dinamically
|
|
}
|
|
else{
|
|
return '<div class="ui-datepicker-select-month" style="display: block">' + monthPicker + '</div>' +
|
|
'<div class="ui-datepicker-select-year" style="display: none"></div>';
|
|
}
|
|
}
|
|
else{
|
|
return '<div class="ui-datepicker-select-month" style="display: none">' + monthPicker + '</div>' +
|
|
'<div class="ui-datepicker-select-year" style="display: block"></div>';
|
|
}
|
|
},
|
|
|
|
_pickMonthYear_MonthYearPicker: function(id, valueMY, period) {
|
|
var dummySelect = $('<select/>').append( new Option(valueMY, valueMY, true, true) );
|
|
//select month and show datepicker or select year ...
|
|
this._selectMonthYear(id, dummySelect[0], period);
|
|
//... and show month picker
|
|
if(period == 'Y' && valueMY != undefined) {
|
|
var target = $(id);
|
|
var inst = this._getInst(target[0]);
|
|
inst.selectedYear = valueMY
|
|
inst.drawYear = valueMY
|
|
this._toggleDisplay_MonthYearPicker(id, 2);
|
|
}
|
|
},
|
|
|
|
_reposition_MonthYearPicker: function (inst) {
|
|
if (inst.inline) {
|
|
return;
|
|
}
|
|
inst.dpDiv.position({
|
|
my: "left top",
|
|
at: "left bottom",
|
|
of: $(inst.input)
|
|
});
|
|
},
|
|
|
|
_addHoverEvents_MonthYearPicker: function (parent) {
|
|
var dpMonths = parent.find('.ui-state-default');
|
|
dpMonths.hover(
|
|
function () {
|
|
$(this).addClass('ui-state-hover');
|
|
},
|
|
function () {
|
|
$(this).removeClass("ui-state-hover");
|
|
});
|
|
},
|
|
|
|
_toggleDisplay_MonthYearPicker: function(inst, screen, input) {
|
|
if(typeof inst == 'string') {
|
|
//var inst = this._curInst;
|
|
var target = $(inst);
|
|
inst = this._getInst(target[0]);
|
|
}
|
|
else {
|
|
//get the input element and put it in the target array
|
|
var target = [ input !== undefined
|
|
? input
|
|
: instActive.inline ? dpDiv.parent()[0] : instActive.input[0]];
|
|
}
|
|
if (this._isDisabledDatepicker(target[0])) {
|
|
return;
|
|
}
|
|
//keep the focus for _doKeyDown to work
|
|
this._instInputFocus_MYP(inst);
|
|
|
|
var minDate = this._getMinMaxDate(inst, 'min');
|
|
var maxDate = this._getMinMaxDate(inst, 'max');
|
|
var drawYear = inst.drawYear; //inst.drawYear = inst.selectedYear = inst.currentYear
|
|
var drawMonth = inst.drawMonth;
|
|
var minYear = minDate ? minDate.getFullYear() : 0; //TODO
|
|
var maxYear = maxDate ? maxDate.getFullYear() : undefined;
|
|
var dpHeader = inst.dpDiv.children('.ui-datepicker-header');
|
|
var dpPrev = dpHeader.children('a.ui-datepicker-prev');
|
|
var dpNext = dpHeader.children('a.ui-datepicker-next');
|
|
var dpTitle = dpHeader.children('.ui-datepicker-title');
|
|
|
|
var self = this;
|
|
|
|
switch (screen) {
|
|
case 2:
|
|
if (self._get(inst, 'show_view') != 'year'){
|
|
//month picker
|
|
var inMinYear = (minYear !== undefined && minYear == drawYear);
|
|
var inMaxYear = (maxYear !== undefined && maxYear == drawYear);
|
|
var _advanceYear_MYP = function(diff) {
|
|
inst.drawYear = (drawYear += diff);
|
|
inst.selectedYear = drawYear;
|
|
dpTitle.children(':first').text(drawYear);
|
|
//update screen
|
|
if(minDate || maxDate) {
|
|
inMinYear = minYear == drawYear;
|
|
inMaxYear = maxYear == drawYear;
|
|
//update month selection
|
|
var monthPicker = self._generateMonthPickerHTML_MonthYearPicker(inst, minDate, maxDate, drawMonth, inMinYear, inMaxYear);
|
|
inst.dpDiv.children('.ui-datepicker-select-month').html(monthPicker);
|
|
}
|
|
_updatePrevNextYear_MYP();
|
|
};
|
|
var _updatePrevNextYear_MYP = function() {
|
|
dpPrev.unbind('click');
|
|
if(!inMinYear) {
|
|
dpPrev.removeClass('ui-state-disabled').click(function() {_advanceYear_MYP(-1); self._instInputFocus_MYP(inst);});
|
|
}
|
|
else {
|
|
dpPrev.addClass('ui-state-disabled');
|
|
}
|
|
dpNext.unbind('click');
|
|
if(!inMaxYear) {
|
|
dpNext.removeClass('ui-state-disabled').click(function() {_advanceYear_MYP(1); self._instInputFocus_MYP(inst);});
|
|
}
|
|
else {
|
|
dpNext.addClass('ui-state-disabled');
|
|
}
|
|
};
|
|
//change title link behaviour
|
|
dpTitle.html('<a href="#" class="ui-datepicker-yearpicker" onclick="jQuery.ui_datepicker._toggleDisplay_MonthYearPicker(\''+'#' + inst.id +'\', 3);return false;">' + drawYear +'</a>');
|
|
//change prev next behaviour
|
|
dpPrev.removeAttr('onclick'); //remove DatePicker's onclick event
|
|
dpNext.removeAttr('onclick'); //remove DatePicker's onclick event
|
|
_updatePrevNextYear_MYP();
|
|
|
|
var dpMonthSelector = inst.dpDiv.find('.ui-datepicker-select-month table');
|
|
this._addHoverEvents_MonthYearPicker(dpMonthSelector);
|
|
if ($.ui_datepicker.select_month != false && self._get(inst, 'show_view') == 'month' && inst.selectedDay != undefined && inst.selectedMonth != undefined && inst.selectedYear != undefined){
|
|
var dateStr = self._formatDate( inst,inst.selectedDay, inst.selectedMonth, inst.selectedYear )
|
|
inst.input.val( dateStr );
|
|
self._hideDatepicker()
|
|
}
|
|
inst.dpDiv.find('table.ui-datepicker-calendar').hide();
|
|
inst.dpDiv.find('.ui-datepicker-select-month').show();
|
|
inst.dpDiv.find('.ui-datepicker-select-year').hide();
|
|
this._reposition_MonthYearPicker(inst);
|
|
}
|
|
else{
|
|
var dateStr = self._formatDate( inst,inst.selectedDay, inst.selectedMonth, inst.selectedYear )
|
|
inst.input.val( dateStr );
|
|
self._hideDatepicker()
|
|
}
|
|
break;
|
|
case 3:
|
|
//year picker
|
|
var year = parseInt(drawYear/10, 10) * 10; //first year in this decade
|
|
//change title link behaviour
|
|
dpTitle.unbind('click');
|
|
//change prev next behaviour
|
|
$.backToActualMonth = function() {
|
|
//var d = new Date();
|
|
//var month = d.getMonth()+1;
|
|
$.ui_datepicker._pickMonthYear_MonthYearPicker('#'+inst.id,drawMonth,'M');
|
|
return false;
|
|
};
|
|
var _updateYearPicker_MYP = function(year) {
|
|
//TODO RTL
|
|
//change title html
|
|
dpTitle.html('<a class="ui-datepicker-title" '+
|
|
'onclick="$.ui_datepicker.select_month = false;return $.backToActualMonth();" '+
|
|
'href="#">'+ year + '-' + (year + 9) + '</a>');
|
|
//change prev next behaviour
|
|
dpPrev.unbind('click');
|
|
dpNext.unbind('click');
|
|
if(year > minYear) {
|
|
dpPrev.removeClass('ui-state-disabled').click(function() {_updateYearPicker_MYP(year-21); self._instInputFocus_MYP(inst);}); //year is 2021 at this point
|
|
}
|
|
else {
|
|
dpPrev.addClass('ui-state-disabled');
|
|
}
|
|
if(maxYear === undefined || year+9 < maxYear) {
|
|
dpNext.removeClass('ui-state-disabled').click(function() {_updateYearPicker_MYP(year-1); self._instInputFocus_MYP(inst);});
|
|
}
|
|
else {
|
|
dpNext.addClass('ui-state-disabled');
|
|
}
|
|
|
|
//generate year picker HTML
|
|
var yearPicker = '<table><tbody><tr>';
|
|
//show years in 4x3 matrix (2009-2020)
|
|
year--; //last year of the previous decade (2009)
|
|
for (var i = 1; i <= 12; i++) {
|
|
unselectable = (minYear !== 'undefined' && year < minYear) ||
|
|
(maxYear !== 'undefined' && year > maxYear);
|
|
//html += '<span class="year'+(i == -1 || i == 10 ? ' old' : '')+(currentYear == year ? ' active' : '')+'">'+year+'</span>';
|
|
yearPicker += '<td class="' +
|
|
(unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable months
|
|
((!unselectable && (i==1 || i==12)) ? ' outoffocus' : '') +
|
|
(year == drawYear ? ' ui-datepicker-today' : '') + '"' +
|
|
(unselectable ? '' : ' onclick="$.ui_datepicker.select_month = false;jQuery.ui_datepicker._pickMonthYear_MonthYearPicker(\'#' + inst.id + '\', ' + year + ', \'Y\');return false;"') + '>' + // actions
|
|
((unselectable ? '<span class="ui-state-default">' + year + '</span>' : '<a class="ui-state-default ' +
|
|
//(month == drawMonth ? ' ui-state-highlight' : '') +
|
|
(year == drawYear ? ' ui-state-active' : '') + // highlight selected day
|
|
//(otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
|
|
'" href="#">' + year + '</a>')) + '</td>'; // display selectable date
|
|
if(i % 4 == 0) {
|
|
yearPicker += '</tr>';
|
|
if(i != 12) {
|
|
yearPicker += '<tr>';
|
|
}
|
|
}
|
|
year++;
|
|
}
|
|
yearPicker += '</tbody></table>';
|
|
$('.ui-datepicker-select-year').html(yearPicker);
|
|
};
|
|
|
|
_updateYearPicker_MYP(year);
|
|
|
|
var dpYearSelector = inst.dpDiv.find('.ui-datepicker-select-year table');
|
|
this._addHoverEvents_MonthYearPicker(dpYearSelector);
|
|
|
|
inst.dpDiv.find('table.ui-datepicker-calendar').hide();
|
|
inst.dpDiv.find('.ui-datepicker-select-month').hide();
|
|
inst.dpDiv.find('.ui-datepicker-select-year').show();
|
|
|
|
this._reposition_MonthYearPicker(inst);
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})(jQuery);
|