/*!
* jQuery UI Calendar @VERSION
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
//>>label: Calendar
//>>group: Widgets
//>>description: Displays a calendar for inline date selection.
//>>docs: http://api.jqueryui.com/calendar/
//>>demos: http://jqueryui.com/calendar/
//>>css.structure: ../../themes/base/core.css
//>>css.structure: ../../themes/base/calendar.css
//>>css.theme: ../../themes/base/theme.css
( function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define( [
"jquery",
"globalize",
"globalize/date",
"globalize-locales",
"../date",
"./button",
"../widget",
"../version",
"../keycode",
"../unique-id",
"../tabbable",
"../escape-selector"
], factory );
} else {
// Browser globals
factory( jQuery, Globalize );
}
}( function( $, Globalize ) {
return $.widget( "ui.calendar", {
version: "@VERSION",
options: {
buttons: [],
classes: {
"ui-calendar": "ui-corner-all",
"ui-calendar-header-first": "ui-corner-left",
"ui-calendar-header-last": "ui-corner-right",
"ui-calendar-prev": "ui-corner-all",
"ui-calendar-next": "ui-corner-all"
},
dateFormat: { date: "short" },
eachDay: $.noop,
labels: {
"datePickerRole": "date picker",
"nextText": "Next",
"prevText": "Prev",
"weekHeader": "Wk"
},
locale: "en",
max: null,
min: null,
numberOfMonths: 1,
showWeek: false,
value: null,
// callbacks
select: null
},
refreshRelatedOptions: {
dateFormat: true,
eachDay: true,
locale: true,
max: true,
min: true,
showWeek: true,
value: true
},
_create: function() {
this.id = this.element.uniqueId().attr( "id" );
this.gridId = this.id;
this.labels = this.options.labels;
this.buttonClickContext = this.element[ 0 ];
this._setLocale( this.options.locale, this.options.dateFormat );
this.date = new $.ui.date( this.options.value, this._calendarDateOptions );
this.viewDate = this.date.clone();
this.viewDate.eachDay = this.options.eachDay;
this._on( this.element, {
"click .ui-calendar-prev": function( event ) {
event.preventDefault();
this.date.adjust( "M", -this.options.numberOfMonths );
this._updateView();
},
"click .ui-calendar-next": function( event ) {
event.preventDefault();
this.date.adjust( "M", this.options.numberOfMonths );
this._updateView();
},
"mousedown .ui-calendar-calendar button": function( event ) {
this._setOption( "value", new Date( $( event.currentTarget ).data( "timestamp" ) ) );
this._updateDayElement( "ui-state-active" );
// Allow datepicker to handle focus
if ( this._trigger( "select", event ) !== false ) {
this.activeDescendant.closest( this.grid ).focus();
event.preventDefault();
}
},
"mouseenter .ui-calendar-header-buttons button": "_hover",
"mouseleave .ui-calendar-header-buttons button": "_hover",
"mouseenter .ui-calendar-calendar button": "_hover",
"mouseleave .ui-calendar-calendar button": "_hover",
"keydown .ui-calendar-calendar": "_handleKeydown"
} );
this._createCalendar();
this._setActiveDescendant();
},
_hover: function( event ) {
this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
},
_handleKeydown: function( event ) {
var pageAltKey = ( event.altKey || event.ctrlKey && event.shiftKey );
switch ( event.keyCode ) {
case $.ui.keyCode.ENTER:
this.activeDescendant.mousedown();
return;
case $.ui.keyCode.PAGE_UP:
this.date.adjust( pageAltKey ? "Y" : "M", -1 );
break;
case $.ui.keyCode.PAGE_DOWN:
this.date.adjust( pageAltKey ? "Y" : "M", 1 );
break;
case $.ui.keyCode.END:
this.date.setDay( this.date.daysInMonth() );
break;
case $.ui.keyCode.HOME:
this.date.setDay( 1 );
break;
case $.ui.keyCode.LEFT:
this.date.adjust( "D", -1 );
break;
case $.ui.keyCode.UP:
this.date.adjust( "D", -7 );
break;
case $.ui.keyCode.RIGHT:
this.date.adjust( "D", 1 );
break;
case $.ui.keyCode.DOWN:
this.date.adjust( "D", 7 );
break;
default:
return;
}
if ( this._needsRefresh() ) {
this._updateView();
this.activeDescendant.closest( this.grid ).focus();
} else {
this._setActiveDescendant();
}
},
_updateView: function() {
if ( this.options.numberOfMonths > 1 && this.date.year() === this.viewDate.year() ) {
this.viewDate.adjust( "M", this.options.numberOfMonths *
( this.date.month() > this.viewDate.month() ? 1 : -1 )
);
} else {
this.viewDate.setTime( this.date.date().getTime() );
}
this.refresh();
},
_needsRefresh: function() {
if ( this.date.month() !== this.viewDate.month() || this.date.year() !== this.viewDate.year() ) {
// Check if the needed day is already present in our grid due
// to eachDay option changes (eg. other-months demo)
return !this._getDateElement( this._getDayId( this.date ) ).length;
}
return false;
},
_setActiveDescendant: function() {
this.activeDescendant = this._updateDayElement( "ui-state-focus" );
},
_updateDayElement: function( state ) {
var id = this._getDayId( this.date ),
button = this._getDateElement( id ).children( "button" );
this.grid.attr( "aria-activedescendant", id );
this._removeClass( this.grid.find( "button." + state ), null, state );
this._addClass( button, null, state );
return button;
},
_getDateElement: function( id ) {
return this.grid.find( "#" + $.ui.escapeSelector( id ) );
},
_setLocale: function( locale, dateFormat ) {
var globalize = new Globalize( locale ),
weekdayShortFormatter = globalize.dateFormatter( { raw: "EEEEEE" } ),
weekdayNarrowFormatter = globalize.dateFormatter( { raw: "EEEEE" } ),
firstDayRaw = globalize.dateFormatter( { raw: "c" } )( new Date( 1970, 0, 3 ) );
this._format = globalize.dateFormatter( dateFormat );
this._parse = globalize.dateParser( dateFormat );
this._calendarDateOptions = {
firstDay: ( 7 - globalize.parseNumber( firstDayRaw ) ),
formatWeekdayShort: function( date ) {
// Return the short weekday if its length is < 3. Otherwise, its narrow form.
var shortWeekday = weekdayShortFormatter( date );
return shortWeekday.length > 3 ? weekdayNarrowFormatter( date ) : shortWeekday;
},
formatWeekdayFull: globalize.dateFormatter( { raw: "EEEE" } ),
formatMonth: globalize.dateFormatter( { raw: "MMMM" } ),
formatWeekOfYear: globalize.dateFormatter( { raw: "w" } ),
parse: this._parse
};
},
_createCalendar: function() {
this.element
.attr( "role", "region" )
.append( this._buildHeaderButtons() );
if ( this.options.numberOfMonths === 1 ) {
this._buildSinglePicker();
} else {
this._buildMultiplePicker();
}
this._addClass( this.element, "ui-calendar", "ui-widget ui-widget-content ui-helper-clearfix" );
this._refreshHeaderButtons();
this._createButtonPane();
this.grid = this.element.find( ".ui-calendar-calendar" );
},
_buildSinglePicker: function() {
var header = this._buildHeader();
this._addClass( header, "ui-calendar-header-first ui-calendar-header-last" );
this.element
.attr( "aria-labelledby", this.gridId + "-title" )
.append( header )
.append( this._buildGrid() );
},
_buildMultiplePicker: function() {
var element, header,
rowBreak = $( "
" ),
currentDate = this.viewDate,
months = this.viewDate.months( this.options.numberOfMonths - 1 ),
labelledBy = [],
i = 0;
for ( ; i < months.length; i++ ) {
// TODO: Shouldn't we pass date as a parameter to build* fns instead of setting this.date?
this.viewDate = months[ i ];
this.gridId = this.id + "-" + i;
labelledBy.push( this.gridId + "-title" );
element = $( "
" );
this._addClass( element, "ui-calendar-group" );
header = this._buildHeader();
this._addClass( header, "ui-calendar-header-" +
( ( months[ i ].first ) ? "first" : ( months[ i ].last ) ? "last" : "middle" )
);
element.appendTo( this.element )
.append( header )
.append( this._buildGrid() );
}
this._addClass( this.element, "ui-calendar-multi" )
._addClass( rowBreak, "ui-calendar-row-break" );
this.element
.attr( "aria-labelledby", labelledBy.join( " " ) )
.append( rowBreak );
this.viewDate = currentDate;
},
_buildHeaderButtons: function() {
var buttons = $( "
" );
this.prevButton = $( "