/*!
- * OOUI v0.29.5
+ * OOUI v0.30.2
* https://www.mediawiki.org/wiki/OOUI
*
- * Copyright 2011–2018 OOUI Team and other contributors.
+ * Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2018-11-08T22:38:07Z
+ * Date: 2019-01-23T01:14:20Z
*/
( function ( OO ) {
'ooui-dialog-process-retry': 'Try again',
// Label for process dialog retry action button, visible when describing only warnings
'ooui-dialog-process-continue': 'Continue',
+ // Label for button in combobox input that triggers its dropdown
+ 'ooui-combobox-button-label': 'Dropdown for combobox',
// Label for the file selection widget's select file button
'ooui-selectfile-button-select': 'Select a file',
// Label for the file selection widget if file selection is not supported
* label: OO.ui.msg( 'ooui-dialog-message-accept' ),
* icon: 'check'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
*
* // A button displaying "OK" in Urdu
* $.i18n().locale = 'ur';
* label: OO.ui.msg( 'ooui-dialog-message-accept' ),
* icon: 'check'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
* } );
*
* @param {string} key Message key
* @return {string} Resolved message
*/
OO.ui.resolveMsg = function ( msg ) {
- if ( $.isFunction( msg ) ) {
+ if ( typeof msg === 'function' ) {
return msg();
}
return msg;
OO.ui.getDefaultOverlay = function () {
if ( !OO.ui.$defaultOverlay ) {
OO.ui.$defaultOverlay = $( '<div>' ).addClass( 'oo-ui-defaultOverlay' );
- $( 'body' ).append( OO.ui.$defaultOverlay );
+ $( document.body ).append( OO.ui.$defaultOverlay );
}
return OO.ui.$defaultOverlay;
};
*/
OO.ui.Element.static.infuse = function ( idOrNode, config ) {
var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, config, false );
+
+ if ( typeof idOrNode === 'string' ) {
+ // IDs deprecated since 0.29.7
+ OO.ui.warnDeprecation(
+ 'Passing a string ID to infuse is deprecated. Use an HTMLElement or jQuery collection instead.'
+ );
+ }
// Verify that the type matches up.
// FIXME: uncomment after T89721 is fixed, see T90929.
/*
OO.ui.Element.static.getDir = function ( obj ) {
var isDoc, isWin;
- if ( obj instanceof jQuery ) {
+ if ( obj instanceof $ ) {
obj = obj[ 0 ];
}
isDoc = obj.nodeType === Node.DOCUMENT_NODE;
var rtlScrollType = null;
function test() {
- var $definer = $( '<div dir="rtl" style="font-size: 14px; width: 1px; height: 1px; position: absolute; top: -1000px; overflow: scroll">A</div>' ),
+ var $definer = $( '<div>' ).attr( {
+ dir: 'rtl',
+ style: 'font-size: 14px; width: 1px; height: 1px; position: absolute; top: -1000px; overflow: scroll;'
+ } ).text( 'A' ),
definer = $definer[ 0 ];
$definer.appendTo( 'body' );
}
}
if ( !$.isEmptyObject( animations ) ) {
+ // eslint-disable-next-line jquery/no-animate
$container.stop( true ).animate( animations, config.duration === undefined ? 'fast' : config.duration );
$container.queue( function ( next ) {
deferred.resolve();
el.removeChild( el.firstChild );
}
// Force reflow
+ // eslint-disable-next-line no-void
void el.offsetHeight;
// Reattach all children
for ( i = 0, len = nodes.length; i < len; i++ ) {
methods = Array.isArray( methods ) ? methods : [ methods ];
for ( i = 0, len = methods.length; i < len; i++ ) {
- if ( $.isFunction( this[ methods[ i ] ] ) ) {
+ if ( typeof this[ methods[ i ] ] === 'function' ) {
support++;
}
}
/**
* The TabIndexedElement class is an attribute mixin used to add additional functionality to an
* element created by another class. The mixin provides a ‘tabIndex’ property, which specifies the
- * order in which users will navigate through the focusable elements via the "tab" key.
+ * order in which users will navigate through the focusable elements via the “tab” key.
*
* @example
* // TabIndexedElement is mixed into the ButtonWidget class
* // to provide a tabIndex property.
* var button1 = new OO.ui.ButtonWidget( {
- * label: 'fourth',
- * tabIndex: 4
- * } );
- * var button2 = new OO.ui.ButtonWidget( {
- * label: 'second',
- * tabIndex: 2
- * } );
- * var button3 = new OO.ui.ButtonWidget( {
- * label: 'third',
- * tabIndex: 3
- * } );
- * var button4 = new OO.ui.ButtonWidget( {
- * label: 'first',
- * tabIndex: 1
- * } );
- * $( 'body' ).append( button1.$element, button2.$element, button3.$element, button4.$element );
+ * label: 'fourth',
+ * tabIndex: 4
+ * } ),
+ * button2 = new OO.ui.ButtonWidget( {
+ * label: 'second',
+ * tabIndex: 2
+ * } ),
+ * button3 = new OO.ui.ButtonWidget( {
+ * label: 'third',
+ * tabIndex: 3
+ * } ),
+ * button4 = new OO.ui.ButtonWidget( {
+ * label: 'first',
+ * tabIndex: 1
+ * } );
+ * $( document.body ).append( button1.$element, button2.$element, button3.$element, button4.$element );
*
* @abstract
* @class
/**
* ButtonElement is often mixed into other classes to generate a button, which is a clickable
- * interface element that can be configured with access keys for accessibility.
+ * interface element that can be configured with access keys for keyboard interaction.
* See the [OOUI documentation on MediaWiki] [1] for examples.
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches#Buttons
* @return {OO.ui.Element} The element, for chaining
*/
OO.ui.mixin.GroupElement.prototype.addItems = function ( items, index ) {
+
+ if ( items.length === 0 ) {
+ return this;
+ }
+
// Mixin method
OO.EmitterList.prototype.addItems.call( this, items, index );
OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
var i, len, item, index;
+ if ( items.length === 0 ) {
+ return this;
+ }
+
// Remove specific items elements
for ( i = 0, len = items.length; i < len; i++ ) {
item = items[ i ];
*/
OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
- label = ( ( typeof label === 'string' || label instanceof jQuery ) && label.length ) || ( label instanceof OO.ui.HtmlSnippet && label.toString().length ) ? label : null;
+ label = ( ( typeof label === 'string' || label instanceof $ ) && label.length ) || ( label instanceof OO.ui.HtmlSnippet && label.toString().length ) ? label : null;
if ( this.label !== label ) {
if ( this.$label ) {
}
} else if ( label instanceof OO.ui.HtmlSnippet ) {
this.$label.html( label.toString() );
- } else if ( label instanceof jQuery ) {
+ } else if ( label instanceof $ ) {
this.$label.empty().append( label );
} else {
this.$label.empty();
* value using a jQuery selection. For example:
*
* // Use a <div> tag instead of a <span>
- * $icon: $("<div>")
+ * $icon: $( '<div>' )
* // Use an existing icon element instead of the one generated by the class
* $icon: this.$element
* // Use an icon element from a child widget
this.icon = null;
this.iconTitle = null;
+ // `iconTitle`s are deprecated since 0.30.0
+ if ( config.iconTitle !== undefined ) {
+ OO.ui.warnDeprecation( 'IconElement: Widgets with iconTitle set are deprecated, use title instead. See T76638 for details.' );
+ }
+
// Initialization
this.setIcon( config.icon || this.constructor.static.icon );
this.setIconTitle( config.iconTitle || this.constructor.static.iconTitle );
* a function that returns title text, or `null` for no title.
* @chainable
* @return {OO.ui.Element} The element, for chaining
+ * @deprecated
*/
OO.ui.mixin.IconElement.prototype.setIconTitle = function ( iconTitle ) {
iconTitle =
}
}
+ // `setIconTitle is deprecated since 0.30.0
+ if ( iconTitle !== null ) {
+ // Avoid a warning when this is called from the constructor with no iconTitle set
+ OO.ui.warnDeprecation( 'IconElement: setIconTitle is deprecated, use setTitle of TitledElement instead. See T76638 for details.' );
+ }
+
return this;
};
this.indicator = null;
this.indicatorTitle = null;
+ // `indicatorTitle`s are deprecated since 0.30.0
+ if ( config.indicatorTitle !== undefined ) {
+ OO.ui.warnDeprecation( 'IndicatorElement: Widgets with indicatorTitle set are deprecated, use title instead. See T76638 for details.' );
+ }
+
// Initialization
this.setIndicator( config.indicator || this.constructor.static.indicator );
this.setIndicatorTitle( config.indicatorTitle || this.constructor.static.indicatorTitle );
* `null` for no indicator title
* @chainable
* @return {OO.ui.Element} The element, for chaining
+ * @deprecated
*/
OO.ui.mixin.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
indicatorTitle =
}
}
+ // `setIndicatorTitle is deprecated since 0.30.0
+ if ( indicatorTitle !== null ) {
+ // Avoid a warning when this is called from the constructor with no indicatorTitle set
+ OO.ui.warnDeprecation( 'IndicatorElement: setIndicatorTitle is deprecated, use setTitle of TitledElement instead. See T76638 for details.' );
+ }
+
return this;
};
*
* The library currently contains the following styling flags for general use:
*
- * - **progressive**: Progressive styling is applied to convey that the widget will move the user forward in a process.
+ * - **progressive**: Progressive styling is applied to convey that the widget will move the user forward in a process.
* - **destructive**: Destructive styling is applied to convey that the widget will remove something.
*
* The flags affect the appearance of the buttons:
* @example
* // FlaggedElement is mixed into ButtonWidget to provide styling flags
* var button1 = new OO.ui.ButtonWidget( {
- * label: 'Progressive',
- * flags: 'progressive'
- * } );
- * var button2 = new OO.ui.ButtonWidget( {
- * label: 'Destructive',
- * flags: 'destructive'
- * } );
- * $( 'body' ).append( button1.$element, button2.$element );
+ * label: 'Progressive',
+ * flags: 'progressive'
+ * } ),
+ * button2 = new OO.ui.ButtonWidget( {
+ * label: 'Destructive',
+ * flags: 'destructive'
+ * } );
+ * $( document.body ).append( button1.$element, button2.$element );
*
* {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
* Please see the [OOUI documentation on MediaWiki] [1] for more information.
* the mouse over the element. Titles are not visible on touch devices.
*
* @example
- * // TitledElement provides a 'title' attribute to the
- * // ButtonWidget class
+ * // TitledElement provides a `title` attribute to the
+ * // ButtonWidget class.
* var button = new OO.ui.ButtonWidget( {
* label: 'Button with Title',
* title: 'I am a button'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
*
* @abstract
* @class
};
/**
- * AccessKeyedElement is mixed into other classes to provide an `accesskey` attribute.
+ * AccessKeyedElement is mixed into other classes to provide an `accesskey` HTML attribute.
* Accesskeys allow an user to go to a specific element by using
* a shortcut combination of a browser specific keys + the key
* set to the field.
*
* @example
- * // AccessKeyedElement provides an 'accesskey' attribute to the
- * // ButtonWidget class
+ * // AccessKeyedElement provides an `accesskey` attribute to the
+ * // ButtonWidget class.
* var button = new OO.ui.ButtonWidget( {
* label: 'Button with Accesskey',
* accessKey: 'k'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
*
* @abstract
* @class
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches
*
* @example
- * // A button widget
+ * // A button widget.
* var button = new OO.ui.ButtonWidget( {
* label: 'Button with Icon',
* icon: 'trash',
* title: 'Remove'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
*
* NOTE: HTML form buttons should use the OO.ui.ButtonInputWidget class.
*
* removed, and cleared from the group.
*
* @example
- * // Example: A ButtonGroupWidget with two buttons
+ * // A ButtonGroupWidget with two buttons.
* var button1 = new OO.ui.PopupButtonWidget( {
- * label: 'Select a category',
- * icon: 'menu',
- * popup: {
- * $content: $( '<p>List of categories...</p>' ),
- * padded: true,
- * align: 'left'
- * }
- * } );
- * var button2 = new OO.ui.ButtonWidget( {
- * label: 'Add item'
- * });
- * var buttonGroup = new OO.ui.ButtonGroupWidget( {
- * items: [button1, button2]
- * } );
- * $( 'body' ).append( buttonGroup.$element );
+ * label: 'Select a category',
+ * icon: 'menu',
+ * popup: {
+ * $content: $( '<p>List of categories…</p>' ),
+ * padded: true,
+ * align: 'left'
+ * }
+ * } ),
+ * button2 = new OO.ui.ButtonWidget( {
+ * label: 'Add item'
+ * } ),
+ * buttonGroup = new OO.ui.ButtonGroupWidget( {
+ * items: [ button1, button2 ]
+ * } );
+ * $( document.body ).append( buttonGroup.$element );
*
* @class
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.GroupElement
+ * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
// Mixin constructors
OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+ OO.ui.mixin.TitledElement.call( this, config );
// Initialization
this.$element.addClass( 'oo-ui-buttonGroupWidget' );
OO.inheritClass( OO.ui.ButtonGroupWidget, OO.ui.Widget );
OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.mixin.GroupElement );
+OO.mixinClass( OO.ui.ButtonGroupWidget, OO.ui.mixin.TitledElement );
/* Static Properties */
* for a list of icons included in the library.
*
* @example
- * // An icon widget with a label
+ * // An IconWidget with a label via LabelWidget.
* var myIcon = new OO.ui.IconWidget( {
- * icon: 'help',
- * title: 'Help'
- * } );
- * // Create a label.
- * var iconLabel = new OO.ui.LabelWidget( {
- * label: 'Help'
- * } );
- * $( 'body' ).append( myIcon.$element, iconLabel.$element );
+ * icon: 'help',
+ * title: 'Help'
+ * } ),
+ * // Create a label.
+ * iconLabel = new OO.ui.LabelWidget( {
+ * label: 'Help'
+ * } );
+ * $( document.body ).append( myIcon.$element, iconLabel.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Icons
*
* indicators included in the library, please see the [OOUI documentation on MediaWiki][1].
*
* @example
- * // Example of an indicator widget
+ * // An indicator widget.
* var indicator1 = new OO.ui.IndicatorWidget( {
- * indicator: 'required'
- * } );
- *
- * // Create a fieldset layout to add a label
- * var fieldset = new OO.ui.FieldsetLayout();
+ * indicator: 'required'
+ * } ),
+ * // Create a fieldset layout to add a label.
+ * fieldset = new OO.ui.FieldsetLayout();
* fieldset.addItems( [
- * new OO.ui.FieldLayout( indicator1, { label: 'A required indicator:' } )
+ * new OO.ui.FieldLayout( indicator1, {
+ * label: 'A required indicator:'
+ * } )
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Indicators
*
* will come into focus when the label is clicked.
*
* @example
- * // Examples of LabelWidgets
+ * // Two LabelWidgets.
* var label1 = new OO.ui.LabelWidget( {
- * label: 'plaintext label'
- * } );
- * var label2 = new OO.ui.LabelWidget( {
- * label: $( '<a href="default.html">jQuery label</a>' )
- * } );
- * // Create a fieldset layout with fields for each example
- * var fieldset = new OO.ui.FieldsetLayout();
+ * label: 'plaintext label'
+ * } ),
+ * label2 = new OO.ui.LabelWidget( {
+ * label: $( '<a>' ).attr( 'href', 'default.html' ).text( 'jQuery label' )
+ * } ),
+ * // Create a fieldset layout with fields for each example.
+ * fieldset = new OO.ui.FieldsetLayout();
* fieldset.addItems( [
* new OO.ui.FieldLayout( label1 ),
* new OO.ui.FieldLayout( label2 )
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* @class
* @extends OO.ui.Widget
* };
*
* var windowManager = new OO.ui.WindowManager();
- * $( 'body' ).append( windowManager.$element );
+ * $( document.body ).append( windowManager.$element );
*
* var dialog = new MessageDialog();
* windowManager.addWindows( [ dialog ] );
// The order matters here. If overflow is not set first, Chrome displays bogus scrollbars. See T157672.
// Forcing a reflow is a smaller workaround than calling reconsiderScrollbars() for this case.
this.$clippable.css( 'overflowX', 'scroll' );
+ // eslint-disable-next-line no-void
void this.$clippable[ 0 ].offsetHeight; // Force reflow
this.$clippable.css( {
width: Math.max( 0, allotedWidth ),
// The order matters here. If overflow is not set first, Chrome displays bogus scrollbars. See T157672.
// Forcing a reflow is a smaller workaround than calling reconsiderScrollbars() for this case.
this.$clippable.css( 'overflowY', 'scroll' );
+ // eslint-disable-next-line no-void
void this.$clippable[ 0 ].offsetHeight; // Force reflow
this.$clippable.css( {
height: Math.max( 0, allotedHeight ),
* Unlike most widgets, PopupWidget is initially hidden and must be shown by calling #toggle.
*
* @example
- * // A popup widget.
+ * // A PopupWidget.
* var popup = new OO.ui.PopupWidget( {
* $content: $( '<p>Hi there!</p>' ),
* padded: true,
* width: 300
* } );
*
- * $( 'body' ).append( popup.$element );
+ * $( document.body ).append( popup.$element );
* // To display the popup, toggle the visibility to 'true'.
* popup.toggle( true );
*
.append( this.$popup, this.$anchor );
// Move content, which was added to #$element by OO.ui.Widget, to the body
// FIXME This is gross, we should use '$body' or something for the config
- if ( config.$content instanceof jQuery ) {
+ if ( config.$content instanceof $ ) {
this.$body.append( config.$content );
}
* which is used to display additional information or options.
*
* @example
- * // Example of a popup button.
+ * // A PopupButtonWidget.
* var popupButton = new OO.ui.PopupButtonWidget( {
* label: 'Popup button with options',
* icon: 'menu',
* }
* } );
* // Append the button to the DOM.
- * $( 'body' ).append( popupButton.$element );
+ * $( document.body ).append( popupButton.$element );
*
* @class
* @extends OO.ui.ButtonWidget
* @mixins OO.ui.mixin.LabelElement
* @mixins OO.ui.mixin.FlaggedElement
* @mixins OO.ui.mixin.AccessKeyedElement
+ * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
OO.ui.mixin.LabelElement.call( this, config );
OO.ui.mixin.FlaggedElement.call( this, config );
OO.ui.mixin.AccessKeyedElement.call( this, config );
+ OO.ui.mixin.TitledElement.call( this, config );
// Properties
this.selected = false;
OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.LabelElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.FlaggedElement );
OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.AccessKeyedElement );
+OO.mixinClass( OO.ui.OptionWidget, OO.ui.mixin.TitledElement );
/* Static Properties */
* information, please see the [OOUI documentation on MediaWiki][1].
*
* @example
- * // Example of a select widget with three options
+ * // A select widget with three options.
* var select = new OO.ui.SelectWidget( {
* items: [
* new OO.ui.OptionWidget( {
* } )
* ]
* } );
- * $( 'body' ).append( select.$element );
+ * $( document.body ).append( select.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
}
return;
}
+ // eslint-disable-next-line no-restricted-properties
if ( String.fromCodePoint ) {
+ // eslint-disable-next-line no-restricted-properties
c = String.fromCodePoint( e.charCode );
} else {
c = String.fromCharCode( e.charCode );
OO.ui.SelectWidget.prototype.getItemMatcher = function ( s, exact ) {
var re;
+ // eslint-disable-next-line no-restricted-properties
if ( s.normalize ) {
+ // eslint-disable-next-line no-restricted-properties
s = s.normalize();
}
s = exact ? s.trim() : s.replace( /^\s+/, '' );
re = new RegExp( re, 'i' );
return function ( item ) {
var matchText = item.getMatchText();
+ // eslint-disable-next-line no-restricted-properties
if ( matchText.normalize ) {
+ // eslint-disable-next-line no-restricted-properties
matchText = matchText.normalize();
}
return re.test( matchText );
/**
* Set the DOM element which has focus while the user is interacting with this SelectWidget.
*
- * Currently this is just used to set `aria-activedescendant` on it.
+ * This is used to set `aria-activedescendant` and `aria-expanded` on it.
*
* @protected
* @param {jQuery} $focusOwner
* [OOUI documentation on MediaWiki][1].
*
* @example
- * // Decorated options in a select widget
+ * // Decorated options in a select widget.
* var select = new OO.ui.SelectWidget( {
* items: [
* new OO.ui.DecoratedOptionWidget( {
* } )
* ]
* } );
- * $( 'body' ).append( select.$element );
+ * $( document.body ).append( select.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* {@link OO.ui.MenuOptionWidget menu options}. MenuSectionOptionWidgets cannot be highlighted or selected.
*
* @example
- * var myDropdown = new OO.ui.DropdownWidget( {
+ * var dropdown = new OO.ui.DropdownWidget( {
* menu: {
* items: [
* new OO.ui.MenuSectionOptionWidget( {
* ]
* }
* } );
- * $( 'body' ).append( myDropdown.$element );
+ * $( document.body ).append( dropdown.$element );
*
* @class
* @extends OO.ui.DecoratedOptionWidget
// TODO: Find a better way to handle post-constructor setup
this.visible = false;
this.$element.addClass( 'oo-ui-element-hidden' );
+ this.$focusOwner.attr( 'aria-expanded', 'false' );
};
/* Setup */
this.scrollItemIntoView( this.items[ 0 ] );
}
+ if ( !anyVisible ) {
+ this.highlightItem( null );
+ }
+
this.$element.toggleClass( 'oo-ui-menuSelectWidget-invisible', !anyVisible );
if ( this.highlightOnFilter ) {
* OO.ui.DropdownInputWidget instead.
*
* @example
- * // Example: A DropdownWidget with a menu that contains three options
+ * // A DropdownWidget with a menu that contains three options.
* var dropDown = new OO.ui.DropdownWidget( {
* label: 'Dropdown menu: Select a menu option',
* menu: {
* }
* } );
*
- * $( 'body' ).append( dropDown.$element );
+ * $( document.body ).append( dropDown.$element );
*
* dropDown.getMenu().selectItemByData( 'b' );
*
- * dropDown.getMenu().findSelectedItem().getData(); // returns 'b'
+ * dropDown.getMenu().findSelectedItem().getData(); // Returns 'b'.
*
* For more information, please see the [OOUI documentation on MediaWiki] [1].
*
OO.ui.DropdownWidget.parent.call( this, config );
// Properties (must be set before TabIndexedElement constructor call)
- this.$handle = $( '<span>' );
+ this.$handle = $( '<button>' );
this.$overlay = ( config.$overlay === true ? OO.ui.getDefaultOverlay() : config.$overlay ) || this.$element;
// Mixin constructors
this.$handle
.addClass( 'oo-ui-dropdownWidget-handle' )
.attr( {
- role: 'combobox',
+ type: 'button',
'aria-owns': this.menu.getElementId(),
- 'aria-autocomplete': 'list'
+ 'aria-haspopup': 'listbox'
} )
.append( this.$icon, this.$label, this.$indicator );
this.$element
selectedLabel = item.getLabel();
// If the label is a DOM element, clone it, because setLabel will append() it
- if ( selectedLabel instanceof jQuery ) {
+ if ( selectedLabel instanceof $ ) {
selectedLabel = selectedLabel.clone();
}
*/
OO.ui.DropdownWidget.prototype.onMenuToggle = function ( isVisible ) {
this.$element.toggleClass( 'oo-ui-dropdownWidget-open', isVisible );
- this.$handle.attr(
- 'aria-expanded',
- this.$element.hasClass( 'oo-ui-dropdownWidget-open' ).toString()
- );
};
/**
* @example
* // A RadioSelectWidget with RadioOptions.
* var option1 = new OO.ui.RadioOptionWidget( {
- * data: 'a',
- * label: 'Selected radio option'
- * } );
- *
- * var option2 = new OO.ui.RadioOptionWidget( {
- * data: 'b',
- * label: 'Unselected radio option'
- * } );
- *
- * var radioSelect=new OO.ui.RadioSelectWidget( {
- * items: [ option1, option2 ]
- * } );
+ * data: 'a',
+ * label: 'Selected radio option'
+ * } ),
+ * option2 = new OO.ui.RadioOptionWidget( {
+ * data: 'b',
+ * label: 'Unselected radio option'
+ * } );
+ * radioSelect = new OO.ui.RadioSelectWidget( {
+ * items: [ option1, option2 ]
+ * } );
*
* // Select 'option 1' using the RadioSelectWidget's selectItem() method.
* radioSelect.selectItem( option1 );
*
- * $( 'body' ).append( radioSelect.$element );
+ * $( document.body ).append( radioSelect.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.ItemWidget
* @mixins OO.ui.mixin.LabelElement
+ * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
// Mixin constructors
OO.ui.mixin.ItemWidget.call( this );
OO.ui.mixin.LabelElement.call( this, config );
+ OO.ui.mixin.TitledElement.call( this, config );
// Properties
this.selected = null;
OO.inheritClass( OO.ui.MultioptionWidget, OO.ui.Widget );
OO.mixinClass( OO.ui.MultioptionWidget, OO.ui.mixin.ItemWidget );
OO.mixinClass( OO.ui.MultioptionWidget, OO.ui.mixin.LabelElement );
+OO.mixinClass( OO.ui.MultioptionWidget, OO.ui.mixin.TitledElement );
/* Events */
* @abstract
* @extends OO.ui.Widget
* @mixins OO.ui.mixin.GroupWidget
+ * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
// Mixin constructors
OO.ui.mixin.GroupWidget.call( this, config );
+ OO.ui.mixin.TitledElement.call( this, config );
// Events
this.aggregate( { change: 'select' } );
OO.inheritClass( OO.ui.MultiselectWidget, OO.ui.Widget );
OO.mixinClass( OO.ui.MultiselectWidget, OO.ui.mixin.GroupWidget );
+OO.mixinClass( OO.ui.MultiselectWidget, OO.ui.mixin.TitledElement );
/* Events */
* @example
* // A CheckboxMultiselectWidget with CheckboxMultioptions.
* var option1 = new OO.ui.CheckboxMultioptionWidget( {
- * data: 'a',
- * selected: true,
- * label: 'Selected checkbox'
- * } );
- *
- * var option2 = new OO.ui.CheckboxMultioptionWidget( {
- * data: 'b',
- * label: 'Unselected checkbox'
- * } );
- *
- * var multiselect=new OO.ui.CheckboxMultiselectWidget( {
- * items: [ option1, option2 ]
- * } );
- *
- * $( 'body' ).append( multiselect.$element );
+ * data: 'a',
+ * selected: true,
+ * label: 'Selected checkbox'
+ * } ),
+ * option2 = new OO.ui.CheckboxMultioptionWidget( {
+ * data: 'b',
+ * label: 'Unselected checkbox'
+ * } ),
+ * multiselect = new OO.ui.CheckboxMultiselectWidget( {
+ * items: [ option1, option2 ]
+ * } );
+ * $( document.body ).append( multiselect.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* } );
* var progressBar2 = new OO.ui.ProgressBarWidget();
*
- * // Create a FieldsetLayout to layout progress bars
+ * // Create a FieldsetLayout to layout progress bars.
* var fieldset = new OO.ui.FieldsetLayout;
* fieldset.addItems( [
- * new OO.ui.FieldLayout( progressBar1, {label: 'Determinate', align: 'top'}),
- * new OO.ui.FieldLayout( progressBar2, {label: 'Indeterminate', align: 'top'})
+ * new OO.ui.FieldLayout( progressBar1, {
+ * label: 'Determinate',
+ * align: 'top'
+ * } ),
+ * new OO.ui.FieldLayout( progressBar2, {
+ * label: 'Indeterminate',
+ * align: 'top'
+ * } )
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* @class
* @extends OO.ui.Widget
};
/**
- * Data widget intended for creating 'hidden'-type inputs.
+ * Data widget intended for creating `<input type="hidden">` inputs.
*
* @class
* @extends OO.ui.Widget
* icon: 'check',
* value: 'check'
* } );
- * $( 'body' ).append( button.$element );
+ * $( document.body ).append( button.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs#Button_inputs
*
* @mixins OO.ui.mixin.IconElement
* @mixins OO.ui.mixin.IndicatorElement
* @mixins OO.ui.mixin.LabelElement
- * @mixins OO.ui.mixin.TitledElement
*
* @constructor
* @param {Object} [config] Configuration options
OO.ui.mixin.IconElement.call( this, config );
OO.ui.mixin.IndicatorElement.call( this, config );
OO.ui.mixin.LabelElement.call( this, config );
- OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
// Initialization
if ( !config.useInputTag ) {
OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IconElement );
OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.IndicatorElement );
OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.LabelElement );
-OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.mixin.TitledElement );
/* Static Properties */
* This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* @example
- * // An example of selected, unselected, and disabled checkbox inputs
- * var checkbox1=new OO.ui.CheckboxInputWidget( {
- * value: 'a',
- * selected: true
- * } );
- * var checkbox2=new OO.ui.CheckboxInputWidget( {
- * value: 'b'
- * } );
- * var checkbox3=new OO.ui.CheckboxInputWidget( {
- * value:'c',
- * disabled: true
- * } );
- * // Create a fieldset layout with fields for each checkbox.
- * var fieldset = new OO.ui.FieldsetLayout( {
- * label: 'Checkboxes'
- * } );
+ * // An example of selected, unselected, and disabled checkbox inputs.
+ * var checkbox1 = new OO.ui.CheckboxInputWidget( {
+ * value: 'a',
+ * selected: true
+ * } ),
+ * checkbox2 = new OO.ui.CheckboxInputWidget( {
+ * value: 'b'
+ * } ),
+ * checkbox3 = new OO.ui.CheckboxInputWidget( {
+ * value:'c',
+ * disabled: true
+ * } ),
+ * // Create a fieldset layout with fields for each checkbox.
+ * fieldset = new OO.ui.FieldsetLayout( {
+ * label: 'Checkboxes'
+ * } );
* fieldset.addItems( [
* new OO.ui.FieldLayout( checkbox1, { label: 'Selected checkbox', align: 'inline' } ),
* new OO.ui.FieldLayout( checkbox2, { label: 'Unselected checkbox', align: 'inline' } ),
* new OO.ui.FieldLayout( checkbox3, { label: 'Disabled checkbox', align: 'inline' } ),
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* are no options. If no `value` configuration option is provided, the first option is selected.
* If you need a state representing no value (no option being selected), use a DropdownWidget.
*
- * This and OO.ui.RadioSelectInputWidget support the same configuration options.
+ * This and OO.ui.RadioSelectInputWidget support similar configuration options.
*
* @example
- * // Example: A DropdownInputWidget with three options
+ * // A DropdownInputWidget with three options.
* var dropdownInput = new OO.ui.DropdownInputWidget( {
* options: [
* { data: 'a', label: 'First' },
- * { data: 'b', label: 'Second'},
- * { data: 'c', label: 'Third' }
+ * { data: 'b', label: 'Second', disabled: true },
+ * { optgroup: 'Group label' },
+ * { data: 'c', label: 'First sub-item)' }
* ]
* } );
- * $( 'body' ).append( dropdownInput.$element );
+ * $( document.body ).append( dropdownInput.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
*
* @constructor
* @param {Object} [config] Configuration options
- * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
+ * @cfg {Object[]} [options=[]] Array of menu options in the format described above.
* @cfg {Object} [dropdown] Configuration options for {@link OO.ui.DropdownWidget DropdownWidget}
* @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where
* the expanded menu is larger than its containing `<div>`. The specified overlay layer is usually on top of the
.addClass( 'oo-ui-dropdownInputWidget' )
.append( this.dropdownWidget.$element );
this.setTabIndexedElement( this.dropdownWidget.$tabIndexed );
+ this.setTitledElement( this.dropdownWidget.$handle );
};
/* Setup */
* Set the internal list of options, used e.g. by setValue() to see which options are allowed.
*
* This method may be called before the parent constructor, so various properties may not be
- * intialized yet.
+ * initialized yet.
*
- * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @param {Object[]} options Array of menu options (see #constructor for details).
* @private
*/
OO.ui.DropdownInputWidget.prototype.setOptionsData = function ( options ) {
- var
- optionWidgets,
+ var optionWidgets, optIndex, opt, previousOptgroup, optionWidget, optValue,
widget = this;
this.optionsDirty = true;
- optionWidgets = options.map( function ( opt ) {
- var optValue;
+ // Go through all the supplied option configs and create either
+ // MenuSectionOption or MenuOption widgets from each.
+ optionWidgets = [];
+ for ( optIndex = 0; optIndex < options.length; optIndex++ ) {
+ opt = options[ optIndex ];
if ( opt.optgroup !== undefined ) {
- return widget.createMenuSectionOptionWidget( opt.optgroup );
+ // Create a <optgroup> menu item.
+ optionWidget = widget.createMenuSectionOptionWidget( opt.optgroup );
+ previousOptgroup = optionWidget;
+
+ } else {
+ // Create a normal <option> menu item.
+ optValue = widget.cleanUpValue( opt.data );
+ optionWidget = widget.createMenuOptionWidget(
+ optValue,
+ opt.label !== undefined ? opt.label : optValue
+ );
}
- optValue = widget.cleanUpValue( opt.data );
- return widget.createMenuOptionWidget(
- optValue,
- opt.label !== undefined ? opt.label : optValue
- );
+ // Disable the menu option if it is itself disabled or if its parent optgroup is disabled.
+ if ( opt.disabled !== undefined ||
+ previousOptgroup instanceof OO.ui.MenuSectionOptionWidget && previousOptgroup.isDisabled() ) {
+ optionWidget.setDisabled( true );
+ }
- } );
+ optionWidgets.push( optionWidget );
+ }
this.dropdownWidget.getMenu().clearItems().addItems( optionWidgets );
};
widget.$input.append( $optionNode );
$optionsContainer = $optionNode;
}
+
+ // Disable the option or optgroup if required.
+ if ( optionWidget.isDisabled() ) {
+ $optionNode.prop( 'disabled', true );
+ }
} );
this.optionsDirty = false;
* new OO.ui.FieldLayout( radio2, { label: 'Unselected', align: 'inline' } ),
* new OO.ui.FieldLayout( radio3, { label: 'Disabled', align: 'inline' } ),
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* of a hidden HTML `input` tag. Please see the [OOUI documentation on MediaWiki][1] for
* more information about input widgets.
*
- * This and OO.ui.DropdownInputWidget support the same configuration options.
+ * This and OO.ui.DropdownInputWidget support similar configuration options.
*
* @example
- * // Example: A RadioSelectInputWidget with three options
+ * // A RadioSelectInputWidget with three options
* var radioSelectInput = new OO.ui.RadioSelectInputWidget( {
* options: [
* { data: 'a', label: 'First' },
* { data: 'c', label: 'Third' }
* ]
* } );
- * $( 'body' ).append( radioSelectInput.$element );
+ * $( document.body ).append( radioSelectInput.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* more information about input widgets.
*
* @example
- * // Example: A CheckboxMultiselectInputWidget with three options
+ * // A CheckboxMultiselectInputWidget with three options.
* var multiselectInput = new OO.ui.CheckboxMultiselectInputWidget( {
* options: [
* { data: 'a', label: 'First' },
- * { data: 'b', label: 'Second'},
+ * { data: 'b', label: 'Second' },
* { data: 'c', label: 'Third' }
* ]
* } );
- * $( 'body' ).append( multiselectInput.$element );
+ * $( document.body ).append( multiselectInput.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* @example
- * // Example of a text input widget
+ * // A TextInputWidget.
* var textInput = new OO.ui.TextInputWidget( {
* value: 'Text input'
* } )
- * $( 'body' ).append( textInput.$element );
+ * $( document.body ).append( textInput.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
this.readOnly = false;
this.required = false;
this.validate = null;
- this.styleHeight = null;
this.scrollWidth = null;
this.setValidation( config.validate );
// Run our checks if the browser thinks the field is valid
if ( this.validate instanceof Function ) {
result = this.validate( this.getValue() );
- if ( result && $.isFunction( result.promise ) ) {
+ if ( result && typeof result.promise === 'function' ) {
return result.promise().then( function ( valid ) {
return rejectOrResolve( valid );
} );
};
/**
+ * SearchInputWidgets are TextInputWidgets with `type="search"` assigned and feature a
+ * {@link OO.ui.mixin.IconElement search icon} by default.
+ * Please see the [OOUI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs#SearchInputWidget
+ *
* @class
* @extends OO.ui.TextInputWidget
*
};
/**
+ * MultilineTextInputWidgets, like HTML textareas, are featuring customization options to
+ * configure number of rows visible. In addition, these widgets can be autosized to fit user
+ * inputs and can show {@link OO.ui.mixin.IconElement icons} and
+ * {@link OO.ui.mixin.IndicatorElement indicators}.
+ * Please see the [OOUI documentation on MediaWiki] [1] for more information and examples.
+ *
+ * This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
+ *
+ * @example
+ * // A MultilineTextInputWidget.
+ * var multilineTextInput = new OO.ui.MultilineTextInputWidget( {
+ * value: 'Text input on multiple lines'
+ * } )
+ * $( 'body' ).append( multilineTextInput.$element );
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs#MultilineTextInputWidget
+ *
* @class
* @extends OO.ui.TextInputWidget
*
// Properties
this.autosize = !!config.autosize;
+ this.styleHeight = null;
this.minRows = config.rows !== undefined ? config.rows : '';
this.maxRows = config.maxRows || Math.max( 2 * ( this.minRows || 0 ), 10 );
* For more information about menus and options, please see the [OOUI documentation on MediaWiki][1].
*
* @example
- * // Example: A ComboBoxInputWidget.
+ * // A ComboBoxInputWidget.
* var comboBox = new OO.ui.ComboBoxInputWidget( {
* value: 'Option 1',
* options: [
* { data: 'Option 3' }
* ]
* } );
- * $( 'body' ).append( comboBox.$element );
+ * $( document.body ).append( comboBox.$element );
*
* @example
* // Example: A ComboBoxInputWidget with additional option labels.
* }
* ]
* } );
- * $( 'body' ).append( comboBox.$element );
+ * $( document.body ).append( comboBox.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Menu_selects_and_options
*
this.$overlay = ( config.$overlay === true ? OO.ui.getDefaultOverlay() : config.$overlay ) || this.$element;
this.dropdownButton = new OO.ui.ButtonWidget( {
classes: [ 'oo-ui-comboBoxInputWidget-dropdownButton' ],
+ label: OO.ui.msg( 'ooui-combobox-button-label' ),
indicator: 'down',
+ invisibleLabel: true,
disabled: this.disabled
} );
this.menu = new OO.ui.MenuSelectWidget( $.extend(
'aria-owns': this.menu.getElementId(),
'aria-autocomplete': 'list'
} );
+ this.dropdownButton.$button.attr( {
+ 'aria-controls': this.menu.getElementId()
+ } );
// Do not override options set via config.menu.items
if ( config.options !== undefined ) {
this.setOptions( config.options );
* }
* );
*
- * $( 'body' ).append( actionFieldLayout.$element );
+ * $( document.body ).append( actionFieldLayout.$element );
*
* @class
* @extends OO.ui.FieldLayout
* label: 'Field Two'
* } )
* ] );
- * $( 'body' ).append( fieldset.$element );
+ * $( document.body ).append( fieldset.$element );
*
* [1]: https://www.mediawiki.org/wiki/OOUI/Layouts/Fields_and_Fieldsets
*
* action: '/api/formhandler',
* method: 'get'
* } )
- * $( 'body' ).append( form.$element );
+ * $( document.body ).append( form.$element );
*
* @class
* @extends OO.ui.Layout
* padded: true,
* $content: $( '<p>A panel layout with padding and a frame.</p>' )
* } );
- * $( 'body' ).append( panel.$element );
+ * $( document.body ).append( panel.$element );
*
* @class
* @extends OO.ui.Layout
* new OO.ui.TextInputWidget( { value: 'Text' } )
* ]
* } );
- * $( 'body' ).append( layout.$element );
+ * $( document.body ).append( layout.$element );
*
* @class
* @extends OO.ui.Layout
* (to adjust the value in increments) to allow the user to enter a number.
*
* @example
- * // Example: A NumberInputWidget.
+ * // A NumberInputWidget.
* var numberInput = new OO.ui.NumberInputWidget( {
* label: 'NumberInputWidget',
* input: { value: 5 },
* min: 1,
* max: 10
* } );
- * $( 'body' ).append( numberInput.$element );
+ * $( document.body ).append( numberInput.$element );
*
* @class
* @extends OO.ui.TextInputWidget