/*!
- * OOjs UI v0.17.10
+ * OOjs UI v0.18.0
* https://www.mediawiki.org/wiki/OOjs_UI
*
* Copyright 2011–2016 OOjs UI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2016-10-03T18:59:01Z
+ * Date: 2016-11-09T00:52:37Z
*/
( function ( OO ) {
};
};
+/**
+ * Puts a console warning with provided message.
+ *
+ * @param {string} message
+ */
+OO.ui.warnDeprecation = function ( message ) {
+ if ( OO.getProp( window, 'console', 'warn' ) !== undefined ) {
+ // eslint-disable-next-line no-console
+ console.warn( message );
+ }
+};
+
/**
* Returns a function, that, when invoked, will only be triggered at most once
* during a given window of time. If called again during that window, it will
return new Date().getTime();
};
-/**
- * Proxy for `node.addEventListener( eventName, handler, true )`.
- *
- * @param {HTMLElement} node
- * @param {string} eventName
- * @param {Function} handler
- * @deprecated since 0.15.0
- */
-OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
- node.addEventListener( eventName, handler, true );
-};
-
-/**
- * Proxy for `node.removeEventListener( eventName, handler, true )`.
- *
- * @param {HTMLElement} node
- * @param {string} eventName
- * @param {Function} handler
- * @deprecated since 0.15.0
- */
-OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
- node.removeEventListener( eventName, handler, true );
-};
-
/**
* Reconstitute a JavaScript object corresponding to a widget created by
* the PHP implementation.
// pick up dynamic state, like focus, value of form inputs, scroll position, etc.
state = cls.static.gatherPreInfuseState( $elem[ 0 ], data );
// rebuild widget
+ // eslint-disable-next-line new-cap
obj = new cls( data );
// now replace old DOM with this new DOM.
if ( top ) {
* For elements with theme logic hooks, this should be called any time there's a state change.
*
* @param {OO.ui.Element} element Element for which to update classes
- * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
*/
OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
var $elements = $( [] ),
this.$button = $button
.addClass( 'oo-ui-buttonElement-button' )
- .attr( { role: 'button' } )
.on( {
mousedown: this.onMouseDownHandler,
keydown: this.onKeyDownHandler,
click: this.onClickHandler,
keypress: this.onKeyPressHandler
} );
+
+ // Add `role="button"` on `<a>` elements, where it's needed
+ // `toUppercase()` is added for XHTML documents
+ if ( this.$button.prop( 'tagName' ).toUpperCase() === 'A' ) {
+ this.$button.attr( 'role', 'button' );
+ }
};
/**
OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.TabIndexedElement );
OO.mixinClass( OO.ui.ButtonWidget, OO.ui.mixin.AccessKeyedElement );
-/* Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.ButtonWidget.prototype.onMouseDown = function ( e ) {
- if ( !this.isDisabled() ) {
- // Remove the tab-index while the button is down to prevent the button from stealing focus
- this.$button.removeAttr( 'tabindex' );
- }
-
- return OO.ui.mixin.ButtonElement.prototype.onMouseDown.call( this, e );
-};
+/* Static Properties */
/**
* @inheritdoc
*/
-OO.ui.ButtonWidget.prototype.onMouseUp = function ( e ) {
- if ( !this.isDisabled() ) {
- // Restore the tab-index after the button is up to restore the button's accessibility
- this.$button.attr( 'tabindex', this.tabIndex );
- }
+OO.ui.ButtonWidget.static.cancelButtonMouseDownEvents = false;
- return OO.ui.mixin.ButtonElement.prototype.onMouseUp.call( this, e );
-};
+/* Methods */
/**
* Get hyperlink location.
return this.value;
};
-/**
- * Set the directionality of the input, either RTL (right-to-left) or LTR (left-to-right).
- *
- * @deprecated since v0.13.1; use #setDir directly
- * @param {boolean} isRTL Directionality is right-to-left
- * @chainable
- */
-OO.ui.InputWidget.prototype.setRTL = function ( isRTL ) {
- this.setDir( isRTL ? 'rtl' : 'ltr' );
- return this;
-};
-
/**
* Set the directionality of the input.
*
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string} [type='text'] The value of the HTML `type` attribute: 'text', 'password', 'search',
- * 'email', 'url', 'date' or 'number'. Ignored if `multiline` is true.
+ * 'email', 'url', 'date', 'month' or 'number'. Ignored if `multiline` is true.
*
* Some values of `type` result in additional behaviors:
*
type: 'text',
labelPosition: 'after'
}, config );
+
if ( config.type === 'search' ) {
+ OO.ui.warnDeprecation( 'TextInputWidget: config.type=\'search\' is deprecated. Use the SearchInputWidget instead. See T148471 for details.' );
if ( config.icon === undefined ) {
config.icon = 'search';
}
// indicator: 'clear' is set dynamically later, depending on value
}
- if ( config.required ) {
- if ( config.indicator === undefined ) {
- config.indicator = 'required';
- }
- }
// Parent constructor
OO.ui.TextInputWidget.parent.call( this, config );
// Properties
this.type = this.getSaneType( config );
this.readOnly = false;
+ this.required = false;
this.multiline = !!config.multiline;
this.autosize = !!config.autosize;
this.minRows = config.rows !== undefined ? config.rows : '';
.addClass( 'oo-ui-textInputWidget oo-ui-textInputWidget-type-' + this.type )
.append( this.$icon, this.$indicator );
this.setReadOnly( !!config.readOnly );
+ this.setRequired( !!config.required );
this.updateSearchIndicator();
if ( config.placeholder !== undefined ) {
this.$input.attr( 'placeholder', config.placeholder );
if ( config.autofocus ) {
this.$input.attr( 'autofocus', 'autofocus' );
}
- if ( config.required ) {
- this.$input.attr( 'required', 'required' );
- this.$input.attr( 'aria-required', 'true' );
- }
if ( config.autocomplete === false ) {
this.$input.attr( 'autocomplete', 'off' );
// Turning off autocompletion also disables "form caching" when the user navigates to a
return this;
};
+/**
+ * Check if the input is {@link #required required}.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isRequired = function () {
+ return this.required;
+};
+
+/**
+ * Set the {@link #required required} state of the input.
+ *
+ * @param {boolean} state Make input required
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setRequired = function ( state ) {
+ this.required = !!state;
+ if ( this.required ) {
+ this.$input
+ .attr( 'required', 'required' )
+ .attr( 'aria-required', 'true' );
+ if ( this.getIndicator() === null ) {
+ this.setIndicator( 'required' );
+ }
+ } else {
+ this.$input
+ .removeAttr( 'required' )
+ .removeAttr( 'aria-required' );
+ if ( this.getIndicator() === 'required' ) {
+ this.setIndicator( null );
+ }
+ }
+ this.updateSearchIndicator();
+ return this;
+};
+
/**
* Support function for making #onElementAttach work across browsers.
*
'email',
'url',
'date',
+ 'month',
'number'
];
return allowedTypes.indexOf( config.type ) !== -1 ? config.type : 'text';
}
};
-/**
- * Check if a value is valid.
- *
- * This method returns a promise that resolves with a boolean `true` if the current value is
- * considered valid according to the supplied {@link #validate validation pattern}.
- *
- * @deprecated since v0.12.3
- * @return {jQuery.Promise} A promise that resolves to a boolean `true` if the value is valid.
- */
-OO.ui.TextInputWidget.prototype.isValid = function () {
- var result;
-
- if ( this.validate instanceof Function ) {
- result = this.validate( this.getValue() );
- if ( result && $.isFunction( result.promise ) ) {
- return result.promise();
- } else {
- return $.Deferred().resolve( !!result ).promise();
- }
- } else {
- return $.Deferred().resolve( !!this.getValue().match( this.validate ) ).promise();
- }
-};
-
/**
* Get the validity of current value.
*
}
};
+/**
+ * @class
+ * @extends OO.ui.TextInputWidget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.SearchInputWidget = function OoUiSearchInputWidget( config ) {
+ config = $.extend( {
+ icon: 'search'
+ }, config );
+
+ // Set type to text so that TextInputWidget doesn't
+ // get stuck in an infinite loop.
+ config.type = 'text';
+
+ // Parent constructor
+ OO.ui.SearchInputWidget.parent.call( this, config );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-textInputWidget-type-search' );
+ this.updateSearchIndicator();
+ this.connect( this, {
+ disable: 'onDisable'
+ } );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.SearchInputWidget, OO.ui.TextInputWidget );
+
+/* Methods */
+
+/**
+ * @inheritdoc
+ * @protected
+ */
+OO.ui.SearchInputWidget.prototype.getInputElement = function () {
+ return $( '<input>' ).attr( 'type', 'search' );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.SearchInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+ if ( e.which === OO.ui.MouseButtons.LEFT ) {
+ // Clear the text field
+ this.setValue( '' );
+ this.$input[ 0 ].focus();
+ return false;
+ }
+};
+
+/**
+ * Update the 'clear' indicator displayed on type: 'search' text
+ * fields, hiding it when the field is already empty or when it's not
+ * editable.
+ */
+OO.ui.SearchInputWidget.prototype.updateSearchIndicator = function () {
+ if ( this.getValue() === '' || this.isDisabled() || this.isReadOnly() ) {
+ this.setIndicator( null );
+ } else {
+ this.setIndicator( 'clear' );
+ }
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.SearchInputWidget.prototype.onChange = function () {
+ OO.ui.SearchInputWidget.parent.prototype.onChange.call( this );
+ this.updateSearchIndicator();
+};
+
+/**
+ * Handle disable events.
+ *
+ * @param {boolean} disabled Element is disabled
+ * @private
+ */
+OO.ui.SearchInputWidget.prototype.onDisable = function () {
+ this.updateSearchIndicator();
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.SearchInputWidget.prototype.setReadOnly = function ( state ) {
+ OO.ui.SearchInputWidget.parent.prototype.setReadOnly.call( this, state );
+ this.updateSearchIndicator();
+ return this;
+};
+
/**
* ComboBoxInputWidgets combine a {@link OO.ui.TextInputWidget text input} (where a value
* can be entered manually) and a {@link OO.ui.MenuSelectWidget menu of options} (from which
OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) {
// Configuration initialization
config = $.extend( {
- indicator: 'down',
autocomplete: false
}, config );
- // For backwards-compatibility with ComboBoxWidget config
- $.extend( config, config.input );
// Parent constructor
OO.ui.ComboBoxInputWidget.parent.call( this, config );
// Properties
this.$overlay = config.$overlay || this.$element;
+ this.dropdownButton = new OO.ui.ButtonWidget( {
+ classes: [ 'oo-ui-comboBoxInputWidget-dropdownButton' ],
+ indicator: 'down',
+ disabled: this.disabled
+ } );
this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend(
{
widget: this,
},
config.menu
) );
- // For backwards-compatibility with ComboBoxWidget
- this.input = this;
// Events
- this.$indicator.on( {
- click: this.onIndicatorClick.bind( this ),
- keypress: this.onIndicatorKeyPress.bind( this )
- } );
this.connect( this, {
change: 'onInputChange',
enter: 'onInputEnter'
} );
+ this.dropdownButton.connect( this, {
+ click: 'onDropdownButtonClick'
+ } );
this.menu.connect( this, {
choose: 'onMenuChoose',
add: 'onMenuItemsChange',
if ( config.options !== undefined ) {
this.setOptions( config.options );
}
- // Extra class for backwards-compatibility with ComboBoxWidget
- this.$element.addClass( 'oo-ui-comboBoxInputWidget oo-ui-comboBoxWidget' );
+ this.$field = $( '<div>' )
+ .addClass( 'oo-ui-comboBoxInputWidget-field' )
+ .append( this.$input, this.dropdownButton.$element );
+ this.$element
+ .addClass( 'oo-ui-comboBoxInputWidget' )
+ .append( this.$field );
this.$overlay.append( this.menu.$element );
this.onMenuItemsChange();
};
};
/**
- * Handle mouse click events.
- *
- * @private
- * @param {jQuery.Event} e Mouse click event
- */
-OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
- if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
- this.menu.toggle();
- this.$input[ 0 ].focus();
- }
- return false;
-};
-
-/**
- * Handle key press events.
+ * Handle input enter events.
*
* @private
- * @param {jQuery.Event} e Key press event
*/
-OO.ui.ComboBoxInputWidget.prototype.onIndicatorKeyPress = function ( e ) {
- if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
- this.menu.toggle();
- this.$input[ 0 ].focus();
- return false;
+OO.ui.ComboBoxInputWidget.prototype.onInputEnter = function () {
+ if ( !this.isDisabled() ) {
+ this.menu.toggle( false );
}
};
/**
- * Handle input enter events.
+ * Handle button click events.
*
* @private
*/
-OO.ui.ComboBoxInputWidget.prototype.onInputEnter = function () {
- if ( !this.isDisabled() ) {
- this.menu.toggle( false );
- }
+OO.ui.ComboBoxInputWidget.prototype.onDropdownButtonClick = function () {
+ this.menu.toggle();
+ this.$input[ 0 ].focus();
};
/**
// Parent method
OO.ui.ComboBoxInputWidget.parent.prototype.setDisabled.call( this, disabled );
+ if ( this.dropdownButton ) {
+ this.dropdownButton.setDisabled( this.isDisabled() );
+ }
if ( this.menu ) {
this.menu.setDisabled( this.isDisabled() );
}
return this;
};
-/**
- * @class
- * @deprecated since 0.13.2; use OO.ui.ComboBoxInputWidget instead
- */
-OO.ui.ComboBoxWidget = OO.ui.ComboBoxInputWidget;
-
/**
* FieldLayouts are used with OO.ui.FieldsetLayout. Each FieldLayout requires a field-widget,
* which is a widget that is specified by reference before any optional configuration settings.
* @constructor
* @param {OO.ui.Widget} fieldWidget Field widget
* @param {OO.ui.ButtonWidget} buttonWidget Button widget
+ * @param {Object} config
*/
OO.ui.ActionFieldLayout = function OoUiActionFieldLayout( fieldWidget, buttonWidget, config ) {
// Allow passing positional parameters inside the config object