X-Git-Url: https://scripts.mit.edu/gitweb/autoinstallsdev/mediawiki.git/blobdiff_plain/19e297c21b10b1b8a3acad5e73fc71dcb35db44a..6932310fd58ebef145fa01eb76edf7150284d8ea:/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js new file mode 100644 index 00000000..9233eef2 --- /dev/null +++ b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js @@ -0,0 +1,837 @@ +( function ( $, mw ) { + + /** + * DateTimeInputWidgets can be used to input a date, a time, or a date and + * time, in either UTC or the user's local timezone. + * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples. + * + * This widget can be used inside a HTML form, such as a OO.ui.FormLayout. + * + * @example + * // Example of a text input widget + * var dateTimeInput = new mw.widgets.datetime.DateTimeInputWidget( {} ) + * $( 'body' ).append( dateTimeInput.$element ); + * + * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs + * + * @class + * @extends OO.ui.InputWidget + * @mixins OO.ui.mixin.IconElement + * @mixins OO.ui.mixin.IndicatorElement + * @mixins OO.ui.mixin.PendingElement + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {string} [type='datetime'] Whether to act like a 'date', 'time', or 'datetime' input. + * Affects values stored in the relevant and the formatting and + * interpretation of values passed to/from getValue() and setValue(). It's up + * to the user to configure the DateTimeFormatter correctly. + * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for + * mw.widgets.datetime.ProlepticGregorianDateTimeFormatter (with 'format' defaulting to + * '@date', '@time', or '@datetime' depending on 'type'), or an + * mw.widgets.datetime.DateTimeFormatter instance to use. + * @cfg {Object|null} [calendar={}] Configuration options for + * mw.widgets.datetime.CalendarWidget; note certain settings will be forced based on the + * settings passed to this widget. Set null to disable the calendar. + * @cfg {boolean} [required=false] Whether a value is required. + * @cfg {boolean} [clearable=true] Whether to provide for blanking the value. + * @cfg {Date|null} [value=null] Default value for the widget + * @cfg {Date|string|null} [min=null] Minimum allowed date + * @cfg {Date|string|null} [max=null] Maximum allowed date + */ + mw.widgets.datetime.DateTimeInputWidget = function MwWidgetsDatetimeDateTimeInputWidget( config ) { + // Configuration initialization + config = $.extend( { + type: 'datetime', + clearable: true, + required: false, + min: null, + max: null, + formatter: {}, + calendar: {} + }, config ); + + // See InputWidget#reusePreInfuseDOM about config.$input + if ( config.$input ) { + config.$input.addClass( 'oo-ui-element-hidden' ); + } + + if ( $.isPlainObject( config.formatter ) && config.formatter.format === undefined ) { + config.formatter.format = '@' + config.type; + } + + // Early properties + this.type = config.type; + + // Parent constructor + mw.widgets.datetime.DateTimeInputWidget[ 'super' ].call( this, config ); + + // Mixin constructors + OO.ui.mixin.IconElement.call( this, config ); + OO.ui.mixin.IndicatorElement.call( this, config ); + OO.ui.mixin.PendingElement.call( this, config ); + + // Properties + this.$handle = $( '' ); + this.$fields = $( '' ); + this.fields = []; + this.clearable = !!config.clearable; + this.required = !!config.required; + + if ( typeof config.min === 'string' ) { + config.min = this.parseDateValue( config.min ); + } + if ( config.min instanceof Date && config.min.getTime() >= -62167219200000 ) { + this.min = config.min; + } else { + this.min = new Date( -62167219200000 ); // 0000-01-01T00:00:00.000Z + } + + if ( typeof config.max === 'string' ) { + config.max = this.parseDateValue( config.max ); + } + if ( config.max instanceof Date && config.max.getTime() <= 253402300799999 ) { + this.max = config.max; + } else { + this.max = new Date( 253402300799999 ); // 9999-12-31T12:59:59.999Z + } + + switch ( this.type ) { + case 'date': + this.min.setUTCHours( 0, 0, 0, 0 ); + this.max.setUTCHours( 23, 59, 59, 999 ); + break; + case 'time': + this.min.setUTCFullYear( 1970, 0, 1 ); + this.max.setUTCFullYear( 1970, 0, 1 ); + break; + } + if ( this.min > this.max ) { + throw new Error( + '"min" (' + this.min.toISOString() + ') must not be greater than ' + + '"max" (' + this.max.toISOString() + ')' + ); + } + + if ( config.formatter instanceof mw.widgets.datetime.DateTimeFormatter ) { + this.formatter = config.formatter; + } else if ( $.isPlainObject( config.formatter ) ) { + this.formatter = new mw.widgets.datetime.ProlepticGregorianDateTimeFormatter( config.formatter ); + } else { + throw new Error( '"formatter" must be an mw.widgets.datetime.DateTimeFormatter or a plain object' ); + } + + if ( this.type === 'time' || config.calendar === null ) { + this.calendar = null; + } else { + config.calendar = $.extend( {}, config.calendar, { + formatter: this.formatter, + widget: this, + min: this.min, + max: this.max + } ); + this.calendar = new mw.widgets.datetime.CalendarWidget( config.calendar ); + } + + // Events + this.$handle.on( { + click: this.onHandleClick.bind( this ) + } ); + this.connect( this, { + change: 'onChange' + } ); + this.formatter.connect( this, { + local: 'onChange' + } ); + if ( this.calendar ) { + this.calendar.connect( this, { + change: 'onCalendarChange' + } ); + } + + // Initialization + this.setTabIndex( -1 ); + + this.$fields.addClass( 'mw-widgets-datetime-dateTimeInputWidget-fields' ); + this.setupFields(); + + this.$handle + .addClass( 'mw-widgets-datetime-dateTimeInputWidget-handle' ) + .append( this.$icon, this.$indicator, this.$fields ); + + this.$element + .addClass( 'mw-widgets-datetime-dateTimeInputWidget' ) + .append( this.$handle ); + + if ( this.calendar ) { + this.$element.append( this.calendar.$element ); + } + }; + + /* Setup */ + + OO.inheritClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.InputWidget ); + OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IconElement ); + OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IndicatorElement ); + OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.PendingElement ); + + /* Static properties */ + + mw.widgets.datetime.DateTimeInputWidget.static.supportsSimpleLabel = false; + + /* Events */ + + /* Methods */ + + /** + * Get the currently focused field, if any + * + * @private + * @return {jQuery} + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.getFocusedField = function () { + return this.$fields.find( this.getElementDocument().activeElement ); + }; + + /** + * Convert a date string to a Date + * + * @private + * @param {string} value + * @return {Date|null} + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.parseDateValue = function ( value ) { + var date, m; + + value = String( value ); + switch ( this.type ) { + case 'date': + value = value + 'T00:00:00Z'; + break; + case 'time': + value = '1970-01-01T' + value + 'Z'; + break; + } + m = /^(\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?Z$/.exec( value ); + if ( m ) { + if ( m[ 7 ] ) { + while ( m[ 7 ].length < 3 ) { + m[ 7 ] += '0'; + } + } else { + m[ 7 ] = 0; + } + date = new Date(); + date.setUTCFullYear( m[ 1 ], m[ 2 ] - 1, m[ 3 ] ); + date.setUTCHours( m[ 4 ], m[ 5 ], m[ 6 ], m[ 7 ] ); + if ( date.getTime() < -62167219200000 || date.getTime() > 253402300799999 || + date.getUTCFullYear() !== +m[ 1 ] || + date.getUTCMonth() + 1 !== +m[ 2 ] || + date.getUTCDate() !== +m[ 3 ] || + date.getUTCHours() !== +m[ 4 ] || + date.getUTCMinutes() !== +m[ 5 ] || + date.getUTCSeconds() !== +m[ 6 ] || + date.getUTCMilliseconds() !== +m[ 7 ] + ) { + date = null; + } + } else { + date = null; + } + + return date; + }; + + /** + * @inheritdoc + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.cleanUpValue = function ( value ) { + var date, pad; + + if ( value === '' ) { + return ''; + } + + if ( value instanceof Date ) { + date = value; + } else { + date = this.parseDateValue( value ); + } + + if ( date instanceof Date ) { + pad = function ( v, l ) { + v = String( v ); + while ( v.length < l ) { + v = '0' + v; + } + return v; + }; + + switch ( this.type ) { + case 'date': + value = pad( date.getUTCFullYear(), 4 ) + + '-' + pad( date.getUTCMonth() + 1, 2 ) + + '-' + pad( date.getUTCDate(), 2 ); + break; + + case 'time': + value = pad( date.getUTCHours(), 2 ) + + ':' + pad( date.getUTCMinutes(), 2 ) + + ':' + pad( date.getUTCSeconds(), 2 ) + + '.' + pad( date.getUTCMilliseconds(), 3 ); + value = value.replace( /\.?0+$/, '' ); + break; + + default: + value = date.toISOString(); + break; + } + } else { + value = ''; + } + + return value; + }; + + /** + * Get the value of the input as a Date object + * + * @return {Date|null} + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.getValueAsDate = function () { + return this.parseDateValue( this.getValue() ); + }; + + /** + * Set up the UI fields + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.setupFields = function () { + var i, $field, spec, placeholder, sz, maxlength, + spanValFunc = function ( v ) { + if ( v === undefined ) { + return this.data( 'mw-widgets-datetime-dateTimeInputWidget-value' ); + } else { + v = String( v ); + this.data( 'mw-widgets-datetime-dateTimeInputWidget-value', v ); + if ( v === '' ) { + v = this.data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder' ); + } + this.text( v ); + return this; + } + }, + reduceFunc = function ( k, v ) { + maxlength = Math.max( maxlength, v ); + }, + disabled = this.isDisabled(), + specs = this.formatter.getFieldSpec(); + + this.$fields.empty(); + this.clearButton = null; + this.fields = []; + + for ( i = 0; i < specs.length; i++ ) { + spec = specs[ i ]; + if ( typeof spec === 'string' ) { + $( '' ) + .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' ) + .text( spec ) + .appendTo( this.$fields ); + continue; + } + + placeholder = ''; + while ( placeholder.length < spec.size ) { + placeholder += '_'; + } + + if ( spec.type === 'number' ) { + // Numbers ''should'' be the same width. But we need some extra for + // IE, apparently. + sz = ( spec.size * 1.15 ) + 'ch'; + } else { + // Add a little for padding + sz = ( spec.size * 1.15 ) + 'ch'; + } + if ( spec.editable && spec.type !== 'static' ) { + if ( spec.type === 'boolean' || spec.type === 'toggleLocal' ) { + $field = $( '' ) + .attr( { + tabindex: disabled ? -1 : 0 + } ) + .width( sz ) + .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder ); + $field.on( { + keydown: this.onFieldKeyDown.bind( this, $field ), + focus: this.onFieldFocus.bind( this, $field ), + click: this.onFieldClick.bind( this, $field ), + 'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field ) + } ); + $field.val = spanValFunc; + } else { + maxlength = spec.size; + if ( spec.intercalarySize ) { + $.each( spec.intercalarySize, reduceFunc ); + } + $field = $( '' ).attr( 'type', 'text' ) + .attr( { + tabindex: disabled ? -1 : 0, + size: spec.size, + maxlength: maxlength + } ) + .prop( { + disabled: disabled, + placeholder: placeholder + } ) + .width( sz ); + $field.on( { + keydown: this.onFieldKeyDown.bind( this, $field ), + click: this.onFieldClick.bind( this, $field ), + focus: this.onFieldFocus.bind( this, $field ), + blur: this.onFieldBlur.bind( this, $field ), + change: this.onFieldChange.bind( this, $field ), + 'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field ) + } ); + } + $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-editField' ); + } else { + $field = $( '' ) + .width( sz ) + .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder ); + if ( spec.type !== 'static' ) { + $field.prop( 'tabIndex', -1 ); + $field.on( 'focus', this.onFieldFocus.bind( this, $field ) ); + } + if ( spec.type === 'static' ) { + $field.text( spec.value ); + } else { + $field.val = spanValFunc; + } + } + + this.fields.push( $field ); + $field + .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' ) + .data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec', spec ) + .appendTo( this.$fields ); + } + + if ( this.clearable ) { + this.clearButton = new OO.ui.ButtonWidget( { + classes: [ 'mw-widgets-datetime-dateTimeInputWidget-field', 'mw-widgets-datetime-dateTimeInputWidget-clearButton' ], + framed: false, + icon: 'trash', + disabled: disabled + } ).connect( this, { + click: 'onClearClick' + } ); + this.$fields.append( this.clearButton.$element ); + } + + this.updateFieldsFromValue(); + }; + + /** + * Update the UI fields from the current value + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.updateFieldsFromValue = function () { + var i, $field, spec, intercalary, sz, + date = this.getValueAsDate(); + + if ( date === null ) { + this.components = null; + + for ( i = 0; i < this.fields.length; i++ ) { + $field = this.fields[ i ]; + spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + $field + .removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid oo-ui-element-hidden' ) + .val( '' ); + + if ( spec.intercalarySize ) { + if ( spec.type === 'number' ) { + // Numbers ''should'' be the same width. But we need some extra for + // IE, apparently. + $field.width( ( spec.size * 1.15 ) + 'ch' ); + } else { + // Add a little for padding + $field.width( ( spec.size * 1.15 ) + 'ch' ); + } + } + } + + this.setFlags( { invalid: this.required } ); + } else { + this.components = this.formatter.getComponentsFromDate( date ); + intercalary = this.components.intercalary; + + for ( i = 0; i < this.fields.length; i++ ) { + $field = this.fields[ i ]; + $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' ); + spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + if ( spec.type !== 'static' ) { + $field.val( spec.formatValue( this.components[ spec.component ] ) ); + } + if ( spec.intercalarySize ) { + if ( intercalary && spec.intercalarySize[ intercalary ] !== undefined ) { + sz = spec.intercalarySize[ intercalary ]; + } else { + sz = spec.size; + } + $field.toggleClass( 'oo-ui-element-hidden', sz <= 0 ); + if ( spec.type === 'number' ) { + // Numbers ''should'' be the same width. But we need some extra for + // IE, apparently. + this.fields[ i ].width( ( sz * 1.15 ) + 'ch' ); + } else { + // Add a little for padding + this.fields[ i ].width( ( sz * 1.15 ) + 'ch' ); + } + } + } + + this.setFlags( { invalid: date < this.min || date > this.max } ); + } + + this.$element.toggleClass( 'mw-widgets-datetime-dateTimeInputWidget-empty', date === null ); + }; + + /** + * Update the value with data from the UI fields + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.updateValueFromFields = function () { + var i, v, $field, spec, curDate, newDate, + components = {}, + anyInvalid = false, + anyEmpty = false, + allEmpty = true; + + for ( i = 0; i < this.fields.length; i++ ) { + $field = this.fields[ i ]; + spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + if ( spec.editable ) { + $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' ); + v = $field.val(); + if ( v === '' ) { + $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' ); + anyEmpty = true; + } else { + allEmpty = false; + v = spec.parseValue( v ); + if ( v === undefined ) { + $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' ); + anyInvalid = true; + } else { + components[ spec.component ] = v; + } + } + } + } + + if ( allEmpty ) { + for ( i = 0; i < this.fields.length; i++ ) { + this.fields[ i ].removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' ); + } + } else if ( anyEmpty ) { + anyInvalid = true; + } + + if ( !anyInvalid ) { + curDate = this.getValueAsDate(); + newDate = this.formatter.getDateFromComponents( components ); + if ( !curDate || !newDate || curDate.getTime() !== newDate.getTime() ) { + this.setValue( newDate ); + } + } + }; + + /** + * Handle change event + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onChange = function () { + var date; + + this.updateFieldsFromValue(); + + if ( this.calendar ) { + date = this.getValueAsDate(); + this.calendar.setSelected( date ); + if ( date ) { + this.calendar.setFocusedDate( date ); + } + } + }; + + /** + * Handle clear button click event + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onClearClick = function () { + this.blur(); + this.setValue( '' ); + }; + + /** + * Handle click on the widget background + * + * @private + * @param {jQuery.Event} e Click event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onHandleClick = function () { + this.focus(); + }; + + /** + * Handle key down events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Key down event + * @return {boolean} False to cancel the default event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldKeyDown = function ( $field, e ) { + var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + if ( !this.isDisabled() ) { + switch ( e.which ) { + case OO.ui.Keys.ENTER: + case OO.ui.Keys.SPACE: + if ( spec.type === 'boolean' ) { + this.setValue( + this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' ) + ); + return false; + } else if ( spec.type === 'toggleLocal' ) { + this.formatter.toggleLocal(); + } + break; + + case OO.ui.Keys.UP: + case OO.ui.Keys.DOWN: + if ( spec.type === 'toggleLocal' ) { + this.formatter.toggleLocal(); + } else { + this.setValue( + this.formatter.adjustComponent( this.getValueAsDate(), spec.component, + e.keyCode === OO.ui.Keys.UP ? -1 : 1, 'wrap' ) + ); + } + if ( $field.is( ':input' ) ) { + $field.select(); + } + return false; + } + } + }; + + /** + * Handle focus events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Focus event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldFocus = function ( $field ) { + var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + if ( !this.isDisabled() ) { + if ( this.getValueAsDate() === null ) { + this.setValue( this.formatter.getDefaultDate() ); + } + if ( $field.is( ':input' ) ) { + $field.select(); + } + + if ( this.calendar ) { + this.calendar.toggle( !!spec.calendarComponent ); + } + } + }; + + /** + * Handle click events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Click event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldClick = function ( $field ) { + var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + if ( !this.isDisabled() ) { + if ( spec.type === 'boolean' ) { + this.setValue( + this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' ) + ); + } else if ( spec.type === 'toggleLocal' ) { + this.formatter.toggleLocal(); + } + } + }; + + /** + * Handle blur events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Blur event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldBlur = function ( $field ) { + var v, date, + spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + this.updateValueFromFields(); + + // Normalize + date = this.getValueAsDate(); + if ( !date ) { + $field.val( '' ); + } else { + v = spec.formatValue( this.formatter.getComponentsFromDate( date )[ spec.component ] ); + if ( v !== $field.val() ) { + $field.val( v ); + } + } + }; + + /** + * Handle change events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Change event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldChange = function () { + this.updateValueFromFields(); + }; + + /** + * Handle wheel events on our field inputs. + * + * @private + * @param {jQuery} $field + * @param {jQuery.Event} e Change event + * @return {boolean} False to cancel the default event + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldWheel = function ( $field, e ) { + var delta = 0, + spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' ); + + if ( this.isDisabled() || !this.getFocusedField().length ) { + return; + } + + // Standard 'wheel' event + if ( e.originalEvent.deltaMode !== undefined ) { + this.sawWheelEvent = true; + } + if ( e.originalEvent.deltaY ) { + delta = -e.originalEvent.deltaY; + } else if ( e.originalEvent.deltaX ) { + delta = e.originalEvent.deltaX; + } + + // Non-standard events + if ( !this.sawWheelEvent ) { + if ( e.originalEvent.wheelDeltaX ) { + delta = -e.originalEvent.wheelDeltaX; + } else if ( e.originalEvent.wheelDeltaY ) { + delta = e.originalEvent.wheelDeltaY; + } else if ( e.originalEvent.wheelDelta ) { + delta = e.originalEvent.wheelDelta; + } else if ( e.originalEvent.detail ) { + delta = -e.originalEvent.detail; + } + } + + if ( delta && spec ) { + if ( spec.type === 'toggleLocal' ) { + this.formatter.toggleLocal(); + } else { + this.setValue( + this.formatter.adjustComponent( this.getValueAsDate(), spec.component, delta < 0 ? -1 : 1, 'wrap' ) + ); + } + return false; + } + }; + + /** + * Handle calendar change event + * + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.onCalendarChange = function () { + var curDate = this.getValueAsDate(), + newDate = this.calendar.getSelected()[ 0 ]; + + if ( newDate ) { + if ( !curDate || newDate.getTime() !== curDate.getTime() ) { + this.setValue( newDate ); + } + } + }; + + /** + * @inheritdoc + * @private + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.getInputElement = function () { + return $( '' ).attr( 'type', 'hidden' ); + }; + + /** + * @inheritdoc + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.setDisabled = function ( disabled ) { + mw.widgets.datetime.DateTimeInputWidget[ 'super' ].prototype.setDisabled.call( this, disabled ); + + // Flag all our fields as disabled + if ( this.$fields ) { + this.$fields.find( 'input' ).prop( 'disabled', this.isDisabled() ); + this.$fields.find( '[tabindex]' ).attr( 'tabindex', this.isDisabled() ? -1 : 0 ); + } + + if ( this.clearButton ) { + this.clearButton.setDisabled( disabled ); + } + + return this; + }; + + /** + * @inheritdoc + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.focus = function () { + if ( !this.getFocusedField().length ) { + this.$fields.find( '.mw-widgets-datetime-dateTimeInputWidget-editField' ).first().focus(); + } + return this; + }; + + /** + * @inheritdoc + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.blur = function () { + this.getFocusedField().blur(); + return this; + }; + + /** + * @inheritdoc + */ + mw.widgets.datetime.DateTimeInputWidget.prototype.simulateLabelClick = function () { + this.focus(); + }; + +}( jQuery, mediaWiki ) );