summaryrefslogtreecommitdiff
path: root/www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js')
-rw-r--r--www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js675
1 files changed, 675 insertions, 0 deletions
diff --git a/www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js b/www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
new file mode 100644
index 00000000..0346cc1e
--- /dev/null
+++ b/www/wiki/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
@@ -0,0 +1,675 @@
+( function ( $, mw ) {
+
+ /**
+ * Provides various methods needed for formatting dates and times. This
+ * implementation implements the proleptic Gregorian calendar over years
+ * 0000–9999.
+ *
+ * @class
+ * @extends mw.widgets.datetime.DateTimeFormatter
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [fullMonthNames] Mapping 1–12 to full month names.
+ * @cfg {Object} [shortMonthNames] Mapping 1–12 to abbreviated month names.
+ * If {@link #fullMonthNames fullMonthNames} is given and this is not,
+ * defaults to the first three characters from that setting.
+ * @cfg {Object} [fullDayNames] Mapping 0–6 to full day of week names. 0 is Sunday, 6 is Saturday.
+ * @cfg {Object} [shortDayNames] Mapping 0–6 to abbreviated day of week names. 0 is Sunday, 6 is Saturday.
+ * If {@link #fullDayNames fullDayNames} is given and this is not, defaults to
+ * the first three characters from that setting.
+ * @cfg {string[]} [dayLetters] Weekday column headers for a calendar. Array of 7 strings.
+ * If {@link #fullDayNames fullDayNames} or {@link #shortDayNames shortDayNames}
+ * are given and this is not, defaults to the first character from
+ * shortDayNames.
+ * @cfg {string[]} [hour12Periods] AM and PM texts. Array of 2 strings, AM and PM.
+ * @cfg {number} [weekStartsOn=0] What day the week starts on: 0 is Sunday, 1 is Monday, 6 is Saturday.
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter = function MwWidgetsDatetimeProlepticGregorianDateTimeFormatter( config ) {
+ this.constructor.static.setupDefaults();
+
+ config = $.extend( {
+ weekStartsOn: 0,
+ hour12Periods: this.constructor.static.hour12Periods
+ }, config );
+
+ if ( config.fullMonthNames && !config.shortMonthNames ) {
+ config.shortMonthNames = {};
+ $.each( config.fullMonthNames, function ( k, v ) {
+ config.shortMonthNames[ k ] = v.substr( 0, 3 );
+ } );
+ }
+ if ( config.shortDayNames && !config.dayLetters ) {
+ config.dayLetters = [];
+ $.each( config.shortDayNames, function ( k, v ) {
+ config.dayLetters[ k ] = v.substr( 0, 1 );
+ } );
+ }
+ if ( config.fullDayNames && !config.dayLetters ) {
+ config.dayLetters = [];
+ $.each( config.fullDayNames, function ( k, v ) {
+ config.dayLetters[ k ] = v.substr( 0, 1 );
+ } );
+ }
+ if ( config.fullDayNames && !config.shortDayNames ) {
+ config.shortDayNames = {};
+ $.each( config.fullDayNames, function ( k, v ) {
+ config.shortDayNames[ k ] = v.substr( 0, 3 );
+ } );
+ }
+ config = $.extend( {
+ fullMonthNames: this.constructor.static.fullMonthNames,
+ shortMonthNames: this.constructor.static.shortMonthNames,
+ fullDayNames: this.constructor.static.fullDayNames,
+ shortDayNames: this.constructor.static.shortDayNames,
+ dayLetters: this.constructor.static.dayLetters
+ }, config );
+
+ // Parent constructor
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].call( this, config );
+
+ // Properties
+ this.weekStartsOn = config.weekStartsOn % 7;
+ this.fullMonthNames = config.fullMonthNames;
+ this.shortMonthNames = config.shortMonthNames;
+ this.fullDayNames = config.fullDayNames;
+ this.shortDayNames = config.shortDayNames;
+ this.dayLetters = config.dayLetters;
+ this.hour12Periods = config.hour12Periods;
+ };
+
+ /* Setup */
+
+ OO.inheritClass( mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, mw.widgets.datetime.DateTimeFormatter );
+
+ /* Static */
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.formats = {
+ '@time': '${hour|0}:${minute|0}:${second|0}',
+ '@date': '$!{dow|short} ${day|#} ${month|short} ${year|#}',
+ '@datetime': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}',
+ '@default': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
+ };
+
+ /**
+ * Default full month names.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.fullMonthNames = null;
+
+ /**
+ * Default abbreviated month names.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.shortMonthNames = null;
+
+ /**
+ * Default full day of week names.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.fullDayNames = null;
+
+ /**
+ * Default abbreviated day of week names.
+ *
+ * @static
+ * @inheritable
+ * @property {Object}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.shortDayNames = null;
+
+ /**
+ * Default day letters.
+ *
+ * @static
+ * @inheritable
+ * @property {string[]}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.dayLetters = null;
+
+ /**
+ * Default AM/PM indicators
+ *
+ * @static
+ * @inheritable
+ * @property {string[]}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.hour12Periods = null;
+
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.static.setupDefaults = function () {
+ mw.widgets.datetime.DateTimeFormatter.static.setupDefaults.call( this );
+
+ if ( this.fullMonthNames && !this.shortMonthNames ) {
+ this.shortMonthNames = {};
+ $.each( this.fullMonthNames, function ( k, v ) {
+ this.shortMonthNames[ k ] = v.substr( 0, 3 );
+ }.bind( this ) );
+ }
+ if ( this.shortDayNames && !this.dayLetters ) {
+ this.dayLetters = [];
+ $.each( this.shortDayNames, function ( k, v ) {
+ this.dayLetters[ k ] = v.substr( 0, 1 );
+ }.bind( this ) );
+ }
+ if ( this.fullDayNames && !this.dayLetters ) {
+ this.dayLetters = [];
+ $.each( this.fullDayNames, function ( k, v ) {
+ this.dayLetters[ k ] = v.substr( 0, 1 );
+ }.bind( this ) );
+ }
+ if ( this.fullDayNames && !this.shortDayNames ) {
+ this.shortDayNames = {};
+ $.each( this.fullDayNames, function ( k, v ) {
+ this.shortDayNames[ k ] = v.substr( 0, 3 );
+ }.bind( this ) );
+ }
+
+ if ( !this.fullMonthNames ) {
+ this.fullMonthNames = {
+ 1: mw.msg( 'january' ),
+ 2: mw.msg( 'february' ),
+ 3: mw.msg( 'march' ),
+ 4: mw.msg( 'april' ),
+ 5: mw.msg( 'may_long' ),
+ 6: mw.msg( 'june' ),
+ 7: mw.msg( 'july' ),
+ 8: mw.msg( 'august' ),
+ 9: mw.msg( 'september' ),
+ 10: mw.msg( 'october' ),
+ 11: mw.msg( 'november' ),
+ 12: mw.msg( 'december' )
+ };
+ }
+ if ( !this.shortMonthNames ) {
+ this.shortMonthNames = {
+ 1: mw.msg( 'jan' ),
+ 2: mw.msg( 'feb' ),
+ 3: mw.msg( 'mar' ),
+ 4: mw.msg( 'apr' ),
+ 5: mw.msg( 'may' ),
+ 6: mw.msg( 'jun' ),
+ 7: mw.msg( 'jul' ),
+ 8: mw.msg( 'aug' ),
+ 9: mw.msg( 'sep' ),
+ 10: mw.msg( 'oct' ),
+ 11: mw.msg( 'nov' ),
+ 12: mw.msg( 'dec' )
+ };
+ }
+
+ if ( !this.fullDayNames ) {
+ this.fullDayNames = {
+ 0: mw.msg( 'sunday' ),
+ 1: mw.msg( 'monday' ),
+ 2: mw.msg( 'tuesday' ),
+ 3: mw.msg( 'wednesday' ),
+ 4: mw.msg( 'thursday' ),
+ 5: mw.msg( 'friday' ),
+ 6: mw.msg( 'saturday' )
+ };
+ }
+ if ( !this.shortDayNames ) {
+ this.shortDayNames = {
+ 0: mw.msg( 'sun' ),
+ 1: mw.msg( 'mon' ),
+ 2: mw.msg( 'tue' ),
+ 3: mw.msg( 'wed' ),
+ 4: mw.msg( 'thu' ),
+ 5: mw.msg( 'fri' ),
+ 6: mw.msg( 'sat' )
+ };
+ }
+ if ( !this.dayLetters ) {
+ this.dayLetters = [];
+ $.each( this.shortDayNames, function ( k, v ) {
+ this.dayLetters[ k ] = v.substr( 0, 1 );
+ }.bind( this ) );
+ }
+
+ if ( !this.hour12Periods ) {
+ this.hour12Periods = [
+ mw.msg( 'period-am' ),
+ mw.msg( 'period-pm' )
+ ];
+ }
+ };
+
+ /* Methods */
+
+ /**
+ * @inheritdoc
+ *
+ * Additional fields implemented here are:
+ * - ${year|#}: Year as a number
+ * - ${year|0}: Year as a number, zero-padded to 4 digits
+ * - ${month|#}: Month as a number
+ * - ${month|0}: Month as a number with leading 0
+ * - ${month|short}: Month from 'shortMonthNames' configuration setting
+ * - ${month|full}: Month from 'fullMonthNames' configuration setting
+ * - ${day|#}: Day of the month as a number
+ * - ${day|0}: Day of the month as a number with leading 0
+ * - ${dow|short}: Day of the week from 'shortDayNames' configuration setting
+ * - ${dow|full}: Day of the week from 'fullDayNames' configuration setting
+ * - ${hour|#}: Hour as a number
+ * - ${hour|0}: Hour as a number with leading 0
+ * - ${hour|12}: Hour in a 12-hour clock as a number
+ * - ${hour|012}: Hour in a 12-hour clock as a number, with leading 0
+ * - ${hour|period}: Value from 'hour12Periods' configuration setting
+ * - ${minute|#}: Minute as a number
+ * - ${minute|0}: Minute as a number with leading 0
+ * - ${second|#}: Second as a number
+ * - ${second|0}: Second as a number with leading 0
+ * - ${millisecond|#}: Millisecond as a number
+ * - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+ var spec = null;
+
+ switch ( tag + '|' + params[ 0 ] ) {
+ case 'year|#':
+ case 'year|0':
+ spec = {
+ component: 'year',
+ calendarComponent: true,
+ type: 'number',
+ size: 4,
+ zeropad: params[ 0 ] === '0'
+ };
+ break;
+
+ case 'month|short':
+ case 'month|full':
+ spec = {
+ component: 'month',
+ calendarComponent: true,
+ type: 'string',
+ values: params[ 0 ] === 'short' ? this.shortMonthNames : this.fullMonthNames
+ };
+ break;
+
+ case 'dow|short':
+ case 'dow|full':
+ spec = {
+ component: 'dow',
+ calendarComponent: true,
+ editable: false,
+ type: 'string',
+ values: params[ 0 ] === 'short' ? this.shortDayNames : this.fullDayNames
+ };
+ break;
+
+ case 'month|#':
+ case 'month|0':
+ case 'day|#':
+ case 'day|0':
+ spec = {
+ component: tag,
+ calendarComponent: true,
+ type: 'number',
+ size: 2,
+ zeropad: params[ 0 ] === '0'
+ };
+ break;
+
+ case 'hour|#':
+ case 'hour|0':
+ case 'minute|#':
+ case 'minute|0':
+ case 'second|#':
+ case 'second|0':
+ spec = {
+ component: tag,
+ calendarComponent: false,
+ type: 'number',
+ size: 2,
+ zeropad: params[ 0 ] === '0'
+ };
+ break;
+
+ case 'hour|12':
+ case 'hour|012':
+ spec = {
+ component: 'hour12',
+ calendarComponent: false,
+ type: 'number',
+ size: 2,
+ zeropad: params[ 0 ] === '012'
+ };
+ break;
+
+ case 'hour|period':
+ spec = {
+ component: 'hour12period',
+ calendarComponent: false,
+ type: 'boolean',
+ values: this.hour12Periods
+ };
+ break;
+
+ case 'millisecond|#':
+ case 'millisecond|0':
+ spec = {
+ component: 'millisecond',
+ calendarComponent: false,
+ type: 'number',
+ size: 3,
+ zeropad: params[ 0 ] === '0'
+ };
+ break;
+
+ default:
+ return mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].prototype.getFieldForTag.call( this, tag, params );
+ }
+
+ if ( spec ) {
+ if ( spec.editable === undefined ) {
+ spec.editable = true;
+ }
+ spec.formatValue = this.formatSpecValue;
+ spec.parseValue = this.parseSpecValue;
+ if ( spec.values ) {
+ spec.size = Math.max.apply(
+ null, $.map( spec.values, function ( v ) { return v.length; } )
+ );
+ }
+ }
+
+ return spec;
+ };
+
+ /**
+ * Get components from a Date object
+ *
+ * Components are:
+ * - year {number}
+ * - month {number} (1-12)
+ * - day {number} (1-31)
+ * - dow {number} (0-6, 0 is Sunday)
+ * - hour {number} (0-23)
+ * - hour12 {number} (1-12)
+ * - hour12period {boolean}
+ * - minute {number} (0-59)
+ * - second {number} (0-59)
+ * - millisecond {number} (0-999)
+ * - zone {number}
+ *
+ * @param {Date|null} date
+ * @return {Object} Components
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+ var ret;
+
+ if ( !( date instanceof Date ) ) {
+ date = this.defaultDate;
+ }
+
+ if ( this.local ) {
+ ret = {
+ year: date.getFullYear(),
+ month: date.getMonth() + 1,
+ day: date.getDate(),
+ dow: date.getDay() % 7,
+ hour: date.getHours(),
+ minute: date.getMinutes(),
+ second: date.getSeconds(),
+ millisecond: date.getMilliseconds(),
+ zone: date.getTimezoneOffset()
+ };
+ } else {
+ ret = {
+ year: date.getUTCFullYear(),
+ month: date.getUTCMonth() + 1,
+ day: date.getUTCDate(),
+ dow: date.getUTCDay() % 7,
+ hour: date.getUTCHours(),
+ minute: date.getUTCMinutes(),
+ second: date.getUTCSeconds(),
+ millisecond: date.getUTCMilliseconds(),
+ zone: 0
+ };
+ }
+
+ ret.hour12period = ret.hour >= 12 ? 1 : 0;
+ ret.hour12 = ret.hour % 12;
+ if ( ret.hour12 === 0 ) {
+ ret.hour12 = 12;
+ }
+
+ return ret;
+ };
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDateFromComponents = function ( components ) {
+ var date = new Date();
+
+ components = $.extend( {}, components );
+ if ( components.hour === undefined && components.hour12 !== undefined && components.hour12period !== undefined ) {
+ components.hour = ( components.hour12 % 12 ) + ( components.hour12period ? 12 : 0 );
+ }
+ components = $.extend( {}, this.getComponentsFromDate( null ), components );
+
+ if ( components.zone ) {
+ // Can't just use the constructor because that's stupid about ancient years.
+ date.setFullYear( components.year, components.month - 1, components.day );
+ date.setHours( components.hour, components.minute, components.second, components.millisecond );
+ } else {
+ // Date.UTC() is stupid about ancient years too.
+ date.setUTCFullYear( components.year, components.month - 1, components.day );
+ date.setUTCHours( components.hour, components.minute, components.second, components.millisecond );
+ }
+
+ return date;
+ };
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.adjustComponent = function ( date, component, delta, mode ) {
+ var min, max, range, components;
+
+ if ( !( date instanceof Date ) ) {
+ date = this.defaultDate;
+ }
+ components = this.getComponentsFromDate( date );
+
+ switch ( component ) {
+ case 'year':
+ min = 0;
+ max = 9999;
+ break;
+ case 'month':
+ min = 1;
+ max = 12;
+ break;
+ case 'day':
+ min = 1;
+ max = this.getDaysInMonth( components.month, components.year );
+ break;
+ case 'hour':
+ min = 0;
+ max = 23;
+ break;
+ case 'minute':
+ case 'second':
+ min = 0;
+ max = 59;
+ break;
+ case 'millisecond':
+ min = 0;
+ max = 999;
+ break;
+ case 'hour12period':
+ component = 'hour';
+ min = 0;
+ max = 23;
+ delta *= 12;
+ break;
+ case 'hour12':
+ component = 'hour';
+ min = components.hour12period ? 12 : 0;
+ max = components.hour12period ? 23 : 11;
+ break;
+ default:
+ return new Date( date.getTime() );
+ }
+
+ components[ component ] += delta;
+ range = max - min + 1;
+ switch ( mode ) {
+ case 'overflow':
+ // Date() will mostly handle it automatically. But months need
+ // manual handling to prevent e.g. Jan 31 => Mar 3.
+ if ( component === 'month' || component === 'year' ) {
+ while ( components.month < 1 ) {
+ components[ component ] += 12;
+ components.year--;
+ }
+ while ( components.month > 12 ) {
+ components[ component ] -= 12;
+ components.year++;
+ }
+ }
+ break;
+ case 'wrap':
+ while ( components[ component ] < min ) {
+ components[ component ] += range;
+ }
+ while ( components[ component ] > max ) {
+ components[ component ] -= range;
+ }
+ break;
+ case 'clip':
+ if ( components[ component ] < min ) {
+ components[ component ] = min;
+ }
+ if ( components[ component ] < max ) {
+ components[ component ] = max;
+ }
+ break;
+ }
+ if ( component === 'month' || component === 'year' ) {
+ components.day = Math.min( components.day, this.getDaysInMonth( components.month, components.year ) );
+ }
+
+ return this.getDateFromComponents( components );
+ };
+
+ /**
+ * Get the number of days in a month
+ *
+ * @protected
+ * @param {number} month
+ * @param {number} year
+ * @return {number}
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDaysInMonth = function ( month, year ) {
+ switch ( month ) {
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return 30;
+ case 2:
+ if ( year % 4 ) {
+ return 28;
+ } else if ( year % 100 ) {
+ return 29;
+ }
+ return ( year % 400 ) ? 28 : 29;
+ default:
+ return 31;
+ }
+ };
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarHeadings = function () {
+ var a = this.dayLetters;
+
+ if ( this.weekStartsOn ) {
+ return a.slice( this.weekStartsOn ).concat( a.slice( 0, this.weekStartsOn ) );
+ } else {
+ return a.slice( 0 ); // clone
+ }
+ };
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+ if ( this.local ) {
+ return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
+ } else {
+ return date1.getUTCFullYear() === date2.getUTCFullYear() && date1.getUTCMonth() === date2.getUTCMonth();
+ }
+ };
+
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarData = function ( date ) {
+ var dt, t, d, e, i, row,
+ getDate = this.local ? 'getDate' : 'getUTCDate',
+ setDate = this.local ? 'setDate' : 'setUTCDate',
+ ret = {
+ dayComponent: 'day',
+ monthComponent: 'month'
+ };
+
+ if ( !( date instanceof Date ) ) {
+ date = this.defaultDate;
+ }
+
+ dt = new Date( date.getTime() );
+ dt[ setDate ]( 1 );
+ t = dt.getTime();
+
+ if ( this.local ) {
+ ret.header = this.fullMonthNames[ dt.getMonth() + 1 ] + ' ' + dt.getFullYear();
+ d = dt.getDay() % 7;
+ e = this.getDaysInMonth( dt.getMonth() + 1, dt.getFullYear() );
+ } else {
+ ret.header = this.fullMonthNames[ dt.getUTCMonth() + 1 ] + ' ' + dt.getUTCFullYear();
+ d = dt.getUTCDay() % 7;
+ e = this.getDaysInMonth( dt.getUTCMonth() + 1, dt.getUTCFullYear() );
+ }
+
+ if ( this.weekStartsOn ) {
+ d = ( d + 7 - this.weekStartsOn ) % 7;
+ }
+ d = 1 - d;
+
+ ret.rows = [];
+ while ( d <= e ) {
+ row = [];
+ for ( i = 0; i < 7; i++, d++ ) {
+ dt = new Date( t );
+ dt[ setDate ]( d );
+ row[ i ] = {
+ display: String( dt[ getDate ]() ),
+ date: dt,
+ extra: d < 1 ? 'prev' : d > e ? 'next' : null
+ };
+ }
+ ret.rows.push( row );
+ }
+
+ return ret;
+ };
+
+}( jQuery, mediaWiki ) );