' )
.addClass( 'mw-widget-calendarWidget-item mw-widget-calendarWidget-day' )
.toggleClass( 'mw-widget-calendarWidget-day-additional', !currentDay.isSame( this.moment, 'month' ) )
.toggleClass( 'mw-widget-calendarWidget-day-today', currentDay.isSame( today, 'day' ) )
.toggleClass( 'mw-widget-calendarWidget-item-selected', currentDay.isSame( selected, 'day' ) )
.text( currentDay.format( 'D' ) )
.data( 'date', currentDay.date() )
.data( 'month', currentDay.month() )
.data( 'year', currentDay.year() )
);
currentDay.add( 1, 'day' );
}
break;
case 'year':
this.labelButton.setLabel( this.moment.format( 'YYYY' ) );
this.upButton.toggle( true );
currentMonth = moment( this.moment ).startOf( 'year' );
for ( i = 0; i < 12; i++ ) {
items.push(
$( '
' )
.addClass( 'mw-widget-calendarWidget-item mw-widget-calendarWidget-month' )
.toggleClass( 'mw-widget-calendarWidget-item-selected', currentMonth.isSame( selected, 'month' ) )
.text( currentMonth.format( 'MMMM' ) )
.data( 'month', currentMonth.month() )
);
currentMonth.add( 1, 'month' );
}
// Shuffle the array to display months in columns rather than rows.
items = [
items[ 0 ], items[ 6 ], // | January | July |
items[ 1 ], items[ 7 ], // | February | August |
items[ 2 ], items[ 8 ], // | March | September |
items[ 3 ], items[ 9 ], // | April | October |
items[ 4 ], items[ 10 ], // | May | November |
items[ 5 ], items[ 11 ] // | June | December |
];
break;
case 'duodecade':
this.labelButton.setLabel( null );
this.upButton.toggle( false );
currentYear = moment( { year: Math.floor( this.moment.year() / 20 ) * 20 } );
for ( i = 0; i < 20; i++ ) {
items.push(
$( '
' )
.addClass( 'mw-widget-calendarWidget-item mw-widget-calendarWidget-year' )
.toggleClass( 'mw-widget-calendarWidget-item-selected', currentYear.isSame( selected, 'year' ) )
.text( currentYear.format( 'YYYY' ) )
.data( 'year', currentYear.year() )
);
currentYear.add( 1, 'year' );
}
break;
}
this.$body.append.apply( this.$body, items );
$bodyWrapper
.removeClass( 'mw-widget-calendarWidget-body-wrapper-fade-up' )
.removeClass( 'mw-widget-calendarWidget-body-wrapper-fade-down' )
.removeClass( 'mw-widget-calendarWidget-body-wrapper-fade-previous' )
.removeClass( 'mw-widget-calendarWidget-body-wrapper-fade-next' );
needsFade = this.previousDisplayLayer !== this.displayLayer;
if ( this.displayLayer === 'month' ) {
needsFade = needsFade || !this.moment.isSame( this.previousMoment, 'month' );
} else if ( this.displayLayer === 'year' ) {
needsFade = needsFade || !this.moment.isSame( this.previousMoment, 'year' );
} else if ( this.displayLayer === 'duodecade' ) {
needsFade = needsFade || (
Math.floor( this.moment.year() / 20 ) * 20 !==
Math.floor( this.previousMoment.year() / 20 ) * 20
);
}
if ( fade && needsFade ) {
this.$oldBody.find( '.mw-widget-calendarWidget-item-selected' )
.removeClass( 'mw-widget-calendarWidget-item-selected' );
if ( fade === 'previous' || fade === 'up' ) {
this.$body.insertBefore( this.$oldBody );
} else if ( fade === 'next' || fade === 'down' ) {
this.$body.insertAfter( this.$oldBody );
}
setTimeout( function () {
$bodyWrapper.addClass( 'mw-widget-calendarWidget-body-wrapper-fade-' + fade );
}.bind( this ), 0 );
} else {
this.$oldBody.replaceWith( this.$body );
}
this.previousMoment = moment( this.moment );
this.previousDisplayLayer = this.displayLayer;
this.previousDate = this.date;
this.$body.on( 'click', this.onBodyClick.bind( this ) );
};
/**
* Handle click events on the "up" button, switching to less precise view.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.onUpButtonClick = function () {
var
layers = this.getDisplayLayers(),
currentLayer = layers.indexOf( this.displayLayer );
if ( currentLayer !== layers.length - 1 ) {
// One layer up
this.displayLayer = layers[ currentLayer + 1 ];
this.updateUI( 'up' );
} else {
this.updateUI();
}
};
/**
* Handle click events on the "previous" button, switching to previous pane.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.onPrevButtonClick = function () {
switch ( this.displayLayer ) {
case 'month':
this.moment.subtract( 1, 'month' );
break;
case 'year':
this.moment.subtract( 1, 'year' );
break;
case 'duodecade':
this.moment.subtract( 20, 'years' );
break;
}
this.updateUI( 'previous' );
};
/**
* Handle click events on the "next" button, switching to next pane.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.onNextButtonClick = function () {
switch ( this.displayLayer ) {
case 'month':
this.moment.add( 1, 'month' );
break;
case 'year':
this.moment.add( 1, 'year' );
break;
case 'duodecade':
this.moment.add( 20, 'years' );
break;
}
this.updateUI( 'next' );
};
/**
* Handle click events anywhere in the body of the widget, which contains the matrix of days,
* months or years to choose. Maybe change the pane or switch to more precise view, depending on
* what gets clicked.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.onBodyClick = function ( e ) {
var
$target = $( e.target ),
layers = this.getDisplayLayers(),
currentLayer = layers.indexOf( this.displayLayer );
if ( $target.data( 'year' ) !== undefined ) {
this.moment.year( $target.data( 'year' ) );
}
if ( $target.data( 'month' ) !== undefined ) {
this.moment.month( $target.data( 'month' ) );
}
if ( $target.data( 'date' ) !== undefined ) {
this.moment.date( $target.data( 'date' ) );
}
if ( currentLayer === 0 ) {
this.setDateFromMoment();
this.updateUI( 'auto' );
} else {
// One layer down
this.displayLayer = layers[ currentLayer - 1 ];
this.updateUI( 'down' );
}
};
/**
* Set the date.
*
* @param {string|null} [date=null] Day or month date, in the format 'YYYY-MM-DD' or 'YYYY-MM'.
* When null, the calendar will show today's date, but not select it. When invalid, the date
* is not changed.
*/
mw.widgets.CalendarWidget.prototype.setDate = function ( date ) {
var mom = date !== null ? moment( date, this.getDateFormat() ) : moment();
if ( mom.isValid() ) {
this.moment = mom;
if ( date !== null ) {
this.setDateFromMoment();
} else if ( this.date !== null ) {
this.date = null;
this.emit( 'change', this.date );
}
this.displayLayer = this.getDisplayLayers()[ 0 ];
this.updateUI();
}
};
/**
* Reset the user interface of this widget to reflect selected date.
*/
mw.widgets.CalendarWidget.prototype.resetUI = function () {
this.moment = this.getDate() !== null ? moment( this.getDate(), this.getDateFormat() ) : moment();
this.displayLayer = this.getDisplayLayers()[ 0 ];
this.updateUI();
};
/**
* Set the date from moment object.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.setDateFromMoment = function () {
// Switch to English locale to avoid number formatting. We want the internal value to be
// '2015-07-24' and not '٢٠١٥-٠٧-٢٤' even if the UI language is Arabic.
var newDate = moment( this.moment ).locale( 'en' ).format( this.getDateFormat() );
if ( this.date !== newDate ) {
this.date = newDate;
this.emit( 'change', this.date );
}
};
/**
* Get current date, in the format 'YYYY-MM-DD' or 'YYYY-MM', depending on precision. Digits will
* not be localised.
*
* @returns {string|null} Date string
*/
mw.widgets.CalendarWidget.prototype.getDate = function () {
return this.date;
};
/**
* Handle focus events.
*
* @private
*/
mw.widgets.CalendarWidget.prototype.onFocus = function () {
this.displayLayer = this.getDisplayLayers()[ 0 ];
this.updateUI( 'down' );
};
/**
* Handle mouse click events.
*
* @private
* @param {jQuery.Event} e Mouse click event
*/
mw.widgets.CalendarWidget.prototype.onClick = function ( e ) {
if ( !this.isDisabled() && e.which === 1 ) {
// Prevent unintended focussing
return false;
}
};
/**
* Handle key down events.
*
* @private
* @param {jQuery.Event} e Key down event
*/
mw.widgets.CalendarWidget.prototype.onKeyDown = function ( e ) {
var
/*jshint -W024*/
dir = OO.ui.Element.static.getDir( this.$element ),
/*jshint +W024*/
nextDirectionKey = dir === 'ltr' ? OO.ui.Keys.RIGHT : OO.ui.Keys.LEFT,
prevDirectionKey = dir === 'ltr' ? OO.ui.Keys.LEFT : OO.ui.Keys.RIGHT,
changed = true;
if ( !this.isDisabled() ) {
switch ( e.which ) {
case prevDirectionKey:
this.moment.subtract( 1, this.precision === 'month' ? 'month' : 'day' );
break;
case nextDirectionKey:
this.moment.add( 1, this.precision === 'month' ? 'month' : 'day' );
break;
case OO.ui.Keys.UP:
this.moment.subtract( 1, this.precision === 'month' ? 'month' : 'week' );
break;
case OO.ui.Keys.DOWN:
this.moment.add( 1, this.precision === 'month' ? 'month' : 'week' );
break;
case OO.ui.Keys.PAGEUP:
this.moment.subtract( 1, this.precision === 'month' ? 'year' : 'month' );
break;
case OO.ui.Keys.PAGEDOWN:
this.moment.add( 1, this.precision === 'month' ? 'year' : 'month' );
break;
default:
changed = false;
break;
}
if ( changed ) {
this.displayLayer = this.getDisplayLayers()[ 0 ];
this.setDateFromMoment();
this.updateUI( 'auto' );
return false;
}
}
};
/**
* @inheritdoc
*/
mw.widgets.CalendarWidget.prototype.toggle = function ( visible ) {
// Parent method
mw.widgets.CalendarWidget.parent.prototype.toggle.call( this, visible );
if ( this.$floatableContainer ) {
this.togglePositioning( this.isVisible() );
}
return this;
};
}( jQuery, mediaWiki ) );