/*!
- * OOjs UI v0.24.4
- * https://www.mediawiki.org/wiki/OOjs_UI
+ * OOUI v0.25.2
+ * https://www.mediawiki.org/wiki/OOUI
*
- * Copyright 2011–2018 OOjs UI Team and other contributors.
+ * Copyright 2011–2018 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2018-01-02T19:08:58Z
+ * Date: 2018-02-07T00:27:24Z
*/
( function ( OO ) {
*/
OO.ui.generateElementId = function () {
OO.ui.elementId++;
- return 'oojsui-' + OO.ui.elementId;
+ return 'ooui-' + OO.ui.elementId;
};
/**
*
* $.i18n().load( languageMap ).done( function() {
* // Replace the built-in `msg` only once we've loaded the internationalization.
- * // OOjs UI uses `OO.ui.deferMsg` for all initially-loaded messages. So long as
+ * // OOUI uses `OO.ui.deferMsg` for all initially-loaded messages. So long as
* // you put off creating any widgets until this promise is complete, no English
* // will be displayed.
* OO.ui.msg = $.i18n;
/**
* Get the default overlay, which is used by various widgets when they are passed `$overlay: true`.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*
* @return {jQuery} Default overlay node
*/
*/
/**
- * Namespace for OOjs UI mixins.
+ * Namespace for OOUI mixins.
*
* Mixins are named according to the type of object they are intended to
* be mixed in to. For example, OO.ui.mixin.GroupElement is intended to be
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string[]} [classes] The names of the CSS classes to apply to the element. CSS styles are added
- * to the top level (e.g., the outermost div) of the element. See the [OOjs UI documentation on MediaWiki][2]
+ * to the top level (e.g., the outermost div) of the element. See the [OOUI documentation on MediaWiki][2]
* for an example.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#cssExample
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches#cssExample
* @cfg {string} [id] The HTML id attribute used in the rendered tag.
* @cfg {string} [text] Text to insert
* @cfg {Array} [content] An array of content elements to append (after #text).
// rebuild widget
// eslint-disable-next-line new-cap
obj = new cls( data );
+ // If anyone is holding a reference to the old DOM element,
+ // let's allow them to OO.ui.infuse() it and do what they expect, see T105828.
+ // Do not use jQuery.data(), as using it on detached nodes leaks memory in 1.x line by design.
+ $elem[ 0 ].oouiInfused = obj.$element;
// now replace old DOM with this new DOM.
if ( top ) {
// An efficient constructor might be able to reuse the entire DOM tree of the original element,
// so only mutate the DOM if we need to.
if ( $elem[ 0 ] !== obj.$element[ 0 ] ) {
$elem.replaceWith( obj.$element );
- // This element is now gone from the DOM, but if anyone is holding a reference to it,
- // let's allow them to OO.ui.infuse() it and do what they expect, see T105828.
- // Do not use jQuery.data(), as using it on detached nodes leaks memory in 1.x line by design.
- $elem[ 0 ].oouiInfused = obj.$element;
}
top.resolve();
}
OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
/**
- * Widgets are compositions of one or more OOjs UI elements that users can both view
+ * Widgets are compositions of one or more OOUI elements that users can both view
* and interact with. All widgets can be configured and modified via a standard API,
* and their state can change dynamically according to a model.
*
/**
* 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.
- * See the [OOjs UI documentation on MediaWiki] [1] for examples.
+ * See the [OOUI documentation on MediaWiki] [1] for examples.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches#Buttons
*
* @abstract
* @class
};
/**
- * Any OOjs UI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
+ * Any OOUI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
* {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
* items from the group is done through the interface the class provides.
- * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ * For more information, please see the [OOUI documentation on MediaWiki] [1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Groups
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Elements/Groups
*
* @abstract
* @mixins OO.EmitterList
};
/**
- * Get an item by its data.
+ * Find an item by its data.
*
* Only the first item with matching data will be returned. To return all matching items,
- * use the #getItemsFromData method.
+ * use the #findItemsFromData method.
*
* @param {Object} data Item data to search for
* @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
*/
-OO.ui.mixin.GroupElement.prototype.getItemFromData = function ( data ) {
+OO.ui.mixin.GroupElement.prototype.findItemFromData = function ( data ) {
var i, len, item,
hash = OO.getHash( data );
};
/**
- * Get items by their data.
+ * Get an item by its data.
*
- * All items with matching data will be returned. To return only the first match, use the #getItemFromData method instead.
+ * @deprecated Since v0.25.0; use {@link #findItemFromData} instead.
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element|null} Item with equivalent data, `null` if none exists
+ */
+OO.ui.mixin.GroupElement.prototype.getItemFromData = function ( data ) {
+ OO.ui.warnDeprecation( 'GroupElement#getItemFromData. Deprecated function. Use findItemFromData instead. See T76630' );
+ return this.findItemFromData( data );
+};
+
+/**
+ * Find items by their data.
+ *
+ * All items with matching data will be returned. To return only the first match, use the #findItemFromData method instead.
*
* @param {Object} data Item data to search for
* @return {OO.ui.Element[]} Items with equivalent data
*/
-OO.ui.mixin.GroupElement.prototype.getItemsFromData = function ( data ) {
+OO.ui.mixin.GroupElement.prototype.findItemsFromData = function ( data ) {
var i, len, item,
hash = OO.getHash( data ),
items = [];
return items;
};
+/**
+ * Find items by their data.
+ *
+ * @deprecated Since v0.25.0; use {@link #findItemsFromData} instead.
+ * @param {Object} data Item data to search for
+ * @return {OO.ui.Element[]} Items with equivalent data
+ */
+OO.ui.mixin.GroupElement.prototype.getItemsFromData = function ( data ) {
+ OO.ui.warnDeprecation( 'GroupElement#getItemsFromData. Deprecated function. Use findItemsFromData instead. See T76630' );
+ return this.findItemsFromData( data );
+};
+
/**
* Add items to the group.
*
* IconElement is often mixed into other classes to generate an icon.
* Icons are graphics, about the size of normal text. They are used to aid the user
* in locating a control or to convey information in a space-efficient way. See the
- * [OOjs UI documentation on MediaWiki] [1] for a list of icons
+ * [OOUI documentation on MediaWiki] [1] for a list of icons
* included in the library.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Icons
*
* @abstract
* @class
* Example of an i18n map:
*
* { default: 'bold-a', en: 'bold-b', de: 'bold-f' }
- * See the [OOjs UI documentation on MediaWiki] [2] for a list of icons included in the library.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ * See the [OOUI documentation on MediaWiki] [2] for a list of icons included in the library.
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Icons
* @cfg {string|Function} [iconTitle] A text string used as the icon title, or a function that returns title
* text. The icon title is displayed when users move the mouse over the icon.
*/
* that opens a menu instead of performing an action directly, for example).
*
* For a list of indicators included in the library, please see the
- * [OOjs UI documentation on MediaWiki] [1].
+ * [OOUI documentation on MediaWiki] [1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Indicators
*
* @abstract
* @class
* @param {Object} [config] Configuration options
* @cfg {jQuery} [$indicator] The indicator element created by the class. If this
* configuration is omitted, the indicator element will use a generated `<span>`.
- * @cfg {string} [indicator] Symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
- * See the [OOjs UI documentation on MediaWiki][2] for a list of indicators included
+ * @cfg {string} [indicator] Symbolic name of the indicator (e.g., ‘clear’ or ‘down’).
+ * See the [OOUI documentation on MediaWiki][2] for a list of indicators included
* in the library.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Indicators
* @cfg {string|Function} [indicatorTitle] A text string used as the indicator title,
* or a function that returns title text. The indicator title is displayed when users move
* the mouse over the indicator.
/* Static Properties */
/**
- * Symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
+ * Symbolic name of the indicator (e.g., ‘clear’ or ‘down’).
* The static property will be overridden if the #indicator configuration is used.
*
* @static
};
/**
- * Set the indicator by its symbolic name: ‘alert’, ‘down’, ‘next’, ‘previous’, ‘required’, ‘up’. Use `null` to remove the indicator.
+ * Set the indicator by its symbolic name: ‘clear’, ‘down’, ‘required’, ‘search’, ‘up’. Use `null` to remove the indicator.
*
* @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
* @chainable
};
/**
- * Get the symbolic name of the indicator (e.g., ‘alert’ or ‘down’).
+ * Get the symbolic name of the indicator (e.g., ‘clear’ or ‘down’).
*
* @return {string} Symbolic name of indicator
*/
/**
* LabelElement is often mixed into other classes to generate a label, which
* helps identify the function of an interface element.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information.
+ * See the [OOUI documentation on MediaWiki] [1] for more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Labels
*
* @abstract
* @class
* configuration is omitted, the label element will use a generated `<span>`.
* @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] The label text. The label can be specified
* as a plaintext string, a jQuery selection of elements, or a function that will produce a string
- * in the future. See the [OOjs UI documentation on MediaWiki] [2] for examples.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ * in the future. See the [OOUI documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Labels
*/
OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
// Configuration initialization
*
* - **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.
- * - **constructive**: Constructive styling is deprecated since v0.23.2 and equivalent to progressive.
*
* The flags affect the appearance of the buttons:
*
* $( '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 [OOjs UI documentation on MediaWiki] [1] for more information.
+ * Please see the [OOUI documentation on MediaWiki] [1] for more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Elements/Flagged
*
* @abstract
* @class
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'progressive' or 'primary') to apply.
- * Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
+ * Please see the [OOUI documentation on MediaWiki] [2] for more information about available flags.
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Elements/Flagged
* @cfg {jQuery} [$flagged] The flagged element. By default,
* the flagged functionality is applied to the element created by the class ($element).
* If a different element is specified, the flagged functionality will be applied to it instead.
/**
* ButtonWidget is a generic widget for buttons. A wide variety of looks,
* feels, and functionality can be customized via the class’s configuration options
- * and methods. Please see the [OOjs UI documentation on MediaWiki] [1] for more information
+ * and methods. Please see the [OOUI documentation on MediaWiki] [1] for more information
* and examples.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches
*
* @example
* // A button widget
/**
* IconWidget is a generic widget for {@link OO.ui.mixin.IconElement icons}. In general, IconWidgets should be used with OO.ui.LabelWidget,
- * which creates a label that identifies the icon’s function. See the [OOjs UI documentation on MediaWiki] [1]
+ * which creates a label that identifies the icon’s function. See the [OOUI documentation on MediaWiki] [1]
* for a list of icons included in the library.
*
* @example
* } );
* $( 'body' ).append( myIcon.$element, iconLabel.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Icons
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Icons
*
* @class
* @extends OO.ui.Widget
/**
* IndicatorWidgets create indicators, which are small graphics that are generally used to draw
- * attention to the status of an item or to clarify the function of a control. For a list of
- * indicators included in the library, please see the [OOjs UI documentation on MediaWiki][1].
+ * attention to the status of an item or to clarify the function within a control. For a list of
+ * indicators included in the library, please see the [OOUI documentation on MediaWiki][1].
*
* @example
* // Example of an indicator widget
* var indicator1 = new OO.ui.IndicatorWidget( {
- * indicator: 'alert'
+ * indicator: 'required'
* } );
*
* // Create a fieldset layout to add a label
* var fieldset = new OO.ui.FieldsetLayout();
* fieldset.addItems( [
- * new OO.ui.FieldLayout( indicator1, { label: 'An alert indicator:' } )
+ * new OO.ui.FieldLayout( indicator1, { label: 'A required indicator:' } )
* ] );
* $( 'body' ).append( fieldset.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Icons,_Indicators,_and_Labels#Indicators
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Indicators
*
* @class
* @extends OO.ui.Widget
} else {
this.$label.on( 'click', function () {
this.input.simulateLabelClick();
- return false;
}.bind( this ) );
}
}
this.$floatableContainer = null;
this.$floatableWindow = null;
this.$floatableClosestScrollable = null;
+ this.floatableOutOfView = false;
this.onFloatableScrollHandler = this.position.bind( this );
this.onFloatableWindowResizeHandler = this.position.bind( this );
elemRect.left <= contRect.right && elemRect.right >= contRect.left;
};
+/**
+ * Check if the floatable is hidden to the user because it was offscreen.
+ *
+ * @return {boolean} Floatable is out of view
+ */
+OO.ui.mixin.FloatableElement.prototype.isFloatableOutOfView = function () {
+ return this.floatableOutOfView;
+};
+
/**
* Position the floatable below its container.
*
return this;
}
- if ( this.hideWhenOutOfView && !this.isElementInViewport( this.$floatableContainer, this.$floatableClosestScrollable ) ) {
+ this.floatableOutOfView = this.hideWhenOutOfView && !this.isElementInViewport( this.$floatableContainer, this.$floatableClosestScrollable );
+ if ( this.floatableOutOfView ) {
this.$floatable.addClass( 'oo-ui-element-hidden' );
return this;
} else {
/**
* PopupWidget is a container for content. The popup is overlaid and positioned absolutely.
* By default, each popup has an anchor that points toward its origin.
- * Please see the [OOjs UI documentation on Mediawiki] [1] for more information and examples.
+ * Please see the [OOUI documentation on Mediawiki] [1] for more information and examples.
*
* Unlike most widgets, PopupWidget is initially hidden and must be shown by calling #toggle.
*
* // To display the popup, toggle the visibility to 'true'.
* popup.toggle( true );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Popups
*
* @class
* @extends OO.ui.Widget
* 'above' and 'below', or between 'before' and 'after', if there is not enough space in the
* desired direction to display the popup without clipping
* @cfg {jQuery} [$container] Constrain the popup to the boundaries of the specified container.
- * See the [OOjs UI docs on MediaWiki][3] for an example.
- * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#containerExample
+ * See the [OOUI docs on MediaWiki][3] for an example.
+ * [3]: https://www.mediawiki.org/wiki/OOUI/Widgets/Popups#containerExample
* @cfg {number} [containerPadding=10] Padding between the popup and its container, specified as a number of pixels.
* @cfg {jQuery} [$content] Content to append to the popup's body
* @cfg {jQuery} [$footer] Content to append to the popup's footer
* @cfg {boolean} [autoClose=false] Automatically close the popup when it loses focus.
* @cfg {jQuery} [$autoCloseIgnore] Elements that will not close the popup when clicked.
- * This config option is only relevant if #autoClose is set to `true`. See the [OOjs UI docs on MediaWiki][2]
+ * This config option is only relevant if #autoClose is set to `true`. See the [OOUI documentation on MediaWiki][2]
* for an example.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#autocloseExample
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Popups#autocloseExample
* @cfg {boolean} [head=false] Show a popup header that contains a #label (if specified) and close
* button.
* @cfg {boolean} [padded=false] Add padding to the popup's body
if ( this.autoFlip ) {
if ( this.popupPosition === 'above' || this.popupPosition === 'below' ) {
- if ( this.isClippedVertically() ) {
+ if ( this.isClippedVertically() || this.isFloatableOutOfView() ) {
// If opening the popup in the normal direction causes it to be clipped, open
// in the opposite one instead
normalHeight = this.$element.height();
this.isAutoFlipped = !this.isAutoFlipped;
this.position();
- if ( this.isClippedVertically() ) {
+ if ( this.isClippedVertically() || this.isFloatableOutOfView() ) {
// If that also causes it to be clipped, open in whichever direction
// we have more space
oppositeHeight = this.$element.height();
}
}
if ( this.popupPosition === 'before' || this.popupPosition === 'after' ) {
- if ( this.isClippedHorizontally() ) {
+ if ( this.isClippedHorizontally() || this.isFloatableOutOfView() ) {
// If opening the popup in the normal direction causes it to be clipped, open
// in the opposite one instead
normalWidth = this.$element.width();
this.toggleClipping( false );
this.position();
this.toggleClipping( true );
- if ( this.isClippedHorizontally() ) {
+ if ( this.isClippedHorizontally() || this.isFloatableOutOfView() ) {
// If that also causes it to be clipped, open in whichever direction
// we have more space
oppositeWidth = this.$element.width();
* @cfg {jQuery} [$overlay] Render the popup into a separate layer. This configuration is useful in cases where
* the expanded popup is larger than its containing `<div>`. The specified overlay layer is usually on top of the
* containing `<div>` and has a larger area. By default, the popup uses relative positioning.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*/
OO.ui.PopupButtonWidget = function OoUiPopupButtonWidget( config ) {
// Configuration initialization
* OptionWidgets are special elements that can be selected and configured with data. The
* data is often unique for each option, but it does not have to be. OptionWidgets are used
* with OO.ui.SelectWidget to create a selection of mutually exclusive options. For more information
- * and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ * and examples, please see the [OOUI documentation on MediaWiki][1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @class
* @extends OO.ui.Widget
* @return {boolean} Item is selectable
*/
OO.ui.OptionWidget.prototype.isSelectable = function () {
- return this.constructor.static.selectable && !this.isDisabled() && this.isVisible();
+ return this.constructor.static.selectable && !this.disabled && this.isVisible();
};
/**
* @return {boolean} Item is highlightable
*/
OO.ui.OptionWidget.prototype.isHighlightable = function () {
- return this.constructor.static.highlightable && !this.isDisabled() && this.isVisible();
+ return this.constructor.static.highlightable && !this.disabled && this.isVisible();
};
/**
* @return {boolean} Item is pressable
*/
OO.ui.OptionWidget.prototype.isPressable = function () {
- return this.constructor.static.pressable && !this.isDisabled() && this.isVisible();
+ return this.constructor.static.pressable && !this.disabled && this.isVisible();
};
/**
};
/**
- * A SelectWidget is of a generic selection of options. The OOjs UI library contains several types of
+ * A SelectWidget is of a generic selection of options. The OOUI library contains several types of
* select widgets, including {@link OO.ui.ButtonSelectWidget button selects},
* {@link OO.ui.RadioSelectWidget radio selects}, and {@link OO.ui.MenuSelectWidget
* menu selects}.
*
* This class should be used together with OO.ui.OptionWidget or OO.ui.DecoratedOptionWidget. For more
- * information, please see the [OOjs UI documentation on MediaWiki][1].
+ * information, please see the [OOUI documentation on MediaWiki][1].
*
* @example
* // Example of a select widget with three options
* } );
* $( 'body' ).append( select.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @abstract
* @class
* @param {Object} [config] Configuration options
* @cfg {OO.ui.OptionWidget[]} [items] An array of options to add to the select.
* Options are created with {@link OO.ui.OptionWidget OptionWidget} classes. See
- * the [OOjs UI documentation on MediaWiki] [2] for examples.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * the [OOUI documentation on MediaWiki] [2] for examples.
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*/
OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
// Configuration initialization
if ( event.target === this.$element[ 0 ] ) {
// This widget was focussed, e.g. by the user tabbing to it.
// The styles for focus state depend on one of the items being selected.
- if ( !this.getSelectedItem() ) {
+ if ( !this.findSelectedItem() ) {
item = this.findFirstSelectableItem();
}
} else {
OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
var nextItem,
handled = false,
- currentItem = this.findHighlightedItem() || this.getSelectedItem();
+ currentItem = this.findHighlightedItem() || this.findSelectedItem();
if ( !this.isDisabled() && this.isVisible() ) {
switch ( e.keyCode ) {
}
this.keyPressBufferTimer = setTimeout( this.clearKeyPressBuffer.bind( this ), 1500 );
- item = this.findHighlightedItem() || this.getSelectedItem();
+ item = this.findHighlightedItem() || this.findSelectedItem();
if ( this.keyPressBuffer === c ) {
// Common (if weird) special case: typing "xxxx" will cycle through all
};
/**
- * Get selected item.
+ * Find selected item.
*
* @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
*/
-OO.ui.SelectWidget.prototype.getSelectedItem = function () {
+OO.ui.SelectWidget.prototype.findSelectedItem = function () {
var i, len;
for ( i = 0, len = this.items.length; i < len; i++ ) {
return null;
};
+/**
+ * Get selected item.
+ *
+ * @deprecated Since v0.25.0; use {@link #findSelectedItem} instead.
+ * @return {OO.ui.OptionWidget|null} Selected item, `null` if no item is selected
+ */
+OO.ui.SelectWidget.prototype.getSelectedItem = function () {
+ OO.ui.warnDeprecation( 'SelectWidget#getSelectedItem: Deprecated function. Use findSelectedItem instead. See T76630.' );
+ return this.findSelectedItem();
+};
+
/**
* Find highlighted item.
*
return null;
};
-/**
- * Get highlighted item.
- *
- * @deprecated 0.23.1 Use {@link #findHighlightedItem} instead.
- * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
- */
-OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
- OO.ui.warnDeprecation( 'SelectWidget#getHighlightedItem: Deprecated function. Use findHighlightedItem instead. See T76630.' );
- return this.findHighlightedItem();
-};
-
/**
* Toggle pressed state.
*
* @chainable
*/
OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
- var itemFromData = this.getItemFromData( data );
+ var itemFromData = this.findItemFromData( data );
if ( data === undefined || !itemFromData ) {
return this.selectItem();
}
return null;
};
-/**
- * Get an option by its position relative to the specified item (or to the start of the option array,
- * if item is `null`). The direction in which to search through the option array is specified with a
- * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
- * `null` if there are no options in the array.
- *
- * @deprecated 0.23.1 Use {@link #findRelativeSelectableItem} instead
- * @param {OO.ui.OptionWidget|null} item Item to describe the start position, or `null` to start at the beginning of the array.
- * @param {number} direction Direction to move in: -1 to move backward, 1 to move forward
- * @param {Function} [filter] Only consider items for which this function returns
- * true. Function takes an OO.ui.OptionWidget and returns a boolean.
- * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
- */
-OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction, filter ) {
- OO.ui.warnDeprecation( 'SelectWidget#getRelativeSelectableItem: Deprecated function. Use findRelativeSelectableItem instead. See T76630.' );
- return this.findRelativeSelectableItem( item, direction, filter );
-};
-
/**
* Find the next selectable item or `null` if there are no selectable items.
* Disabled options and menu-section markers and breaks are not selectable.
return this.findRelativeSelectableItem( null, 1 );
};
-/**
- * Get the next selectable item or `null` if there are no selectable items.
- * Disabled options and menu-section markers and breaks are not selectable.
- *
- * @deprecated 0.23.1 Use {@link OO.ui.SelectWidget#findFirstSelectableItem} instead.
- * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
- */
-OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
- OO.ui.warnDeprecation( 'SelectWidget#getFirstSelectableItem: Deprecated function. Use findFirstSelectableItem instead. See T76630.' );
- return this.findFirstSelectableItem();
-};
-
/**
* Add an array of options to the select. Optionally, an index number can be used to
* specify an insertion point.
* with an {@link OO.ui.mixin.IconElement icon} and/or {@link OO.ui.mixin.IndicatorElement indicator}.
* This class is used with OO.ui.SelectWidget to create a selection of mutually exclusive
* options. For more information about options and selects, please see the
- * [OOjs UI documentation on MediaWiki][1].
+ * [OOUI documentation on MediaWiki][1].
*
* @example
* // Decorated options in a select widget
* } );
* $( 'body' ).append( select.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @class
* @extends OO.ui.OptionWidget
/**
* MenuOptionWidget is an option widget that looks like a menu item. The class is used with
* OO.ui.MenuSelectWidget to create a menu of mutually exclusive options. Please see
- * the [OOjs UI documentation on MediaWiki] [1] for more information.
+ * the [OOUI documentation on MediaWiki] [1] for more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Menu_selects_and_options
*
* @class
* @extends OO.ui.DecoratedOptionWidget
*
* Unlike most widgets, MenuSelectWidget is initially hidden and must be shown by calling #toggle.
*
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * Please see the [OOUI documentation on MediaWiki][1] for more information.
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @class
* @extends OO.ui.SelectWidget
* @inheritdoc
*/
OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
- var currentItem = this.findHighlightedItem() || this.getSelectedItem();
+ var currentItem = this.findHighlightedItem() || this.findSelectedItem();
if ( !this.isDisabled() && this.isVisible() ) {
switch ( e.keyCode ) {
this.togglePositioning( !!this.$floatableContainer );
this.toggleClipping( true );
- if ( this.isClippedVertically() ) {
+ if ( this.isClippedVertically() || this.isFloatableOutOfView() ) {
// If opening the menu downwards causes it to be clipped, flip it to open upwards instead
belowHeight = this.$element.height();
this.setVerticalPosition( 'above' );
- if ( this.isClippedVertically() ) {
+ if ( this.isClippedVertically() || this.isFloatableOutOfView() ) {
// If opening upwards also causes it to be clipped, flip it to open in whichever direction
// we have more space
aboveHeight = this.$element.height();
this.$focusOwner.attr( 'aria-expanded', 'true' );
- if ( this.getSelectedItem() ) {
- this.$focusOwner.attr( 'aria-activedescendant', this.getSelectedItem().getElementId() );
- this.getSelectedItem().scrollElementIntoView( { duration: 0 } );
+ if ( this.findSelectedItem() ) {
+ this.$focusOwner.attr( 'aria-activedescendant', this.findSelectedItem().getElementId() );
+ this.findSelectedItem().scrollElementIntoView( { duration: 0 } );
}
// Auto-hide
*
* dropDown.getMenu().selectItemByData( 'b' );
*
- * dropDown.getMenu().getSelectedItem().getData(); // returns 'b'
+ * dropDown.getMenu().findSelectedItem().getData(); // returns 'b'
*
- * For more information, please see the [OOjs UI documentation on MediaWiki] [1].
+ * For more information, please see the [OOUI documentation on MediaWiki] [1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Menu_selects_and_options
*
* @class
* @extends OO.ui.Widget
* @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
* containing `<div>` and has a larger area. By default, the menu uses relative positioning.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*/
OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
// Configuration initialization
/**
* RadioOptionWidget is an option widget that looks like a radio button.
* The class is used with OO.ui.RadioSelectWidget to create a selection of radio options.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ * Please see the [OOUI documentation on MediaWiki] [1] for more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_option
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Button_selects_and_option
*
* @class
* @extends OO.ui.OptionWidget
* RadioSelectWidget is a {@link OO.ui.SelectWidget select widget} that contains radio
* options and is used together with OO.ui.RadioOptionWidget. The RadioSelectWidget provides
* an interface for adding, removing and selecting options.
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ * Please see the [OOUI documentation on MediaWiki][1] for more information.
*
* If you want to use this within an HTML form, such as a OO.ui.FormLayout, use
* OO.ui.RadioSelectInputWidget instead.
*
* $( 'body' ).append( radioSelect.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @class
* MultioptionWidgets are special elements that can be selected and configured with data. The
* data is often unique for each option, but it does not have to be. MultioptionWidgets are used
* with OO.ui.SelectWidget to create a selection of mutually exclusive options. For more information
- * and examples, please see the [OOjs UI documentation on MediaWiki][1].
+ * and examples, please see the [OOUI documentation on MediaWiki][1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Multioptions
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Multioptions
*
* @class
* @extends OO.ui.Widget
/**
* MultiselectWidget allows selecting multiple options from a list.
*
- * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
+ * For more information about menus and options, please see the [OOUI documentation on MediaWiki][1].
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Menu_selects_and_options
*
* @class
* @abstract
/* Methods */
/**
- * Get options that are selected.
+ * Find options that are selected.
*
* @return {OO.ui.MultioptionWidget[]} Selected options
*/
-OO.ui.MultiselectWidget.prototype.getSelectedItems = function () {
+OO.ui.MultiselectWidget.prototype.findSelectedItems = function () {
return this.items.filter( function ( item ) {
return item.isSelected();
} );
};
/**
- * Get the data of options that are selected.
+ * Get options that are selected.
+ *
+ * @deprecated Since v0.25.0; use {@link #findSelectedItems} instead.
+ * @return {OO.ui.MultioptionWidget[]} Selected options
+ */
+OO.ui.MultiselectWidget.prototype.getSelectedItems = function () {
+ OO.ui.warnDeprecation( 'MultiselectWidget#getSelectedItems: Deprecated function. Use findSelectedItems instead. See T76630.' );
+ return this.findSelectedItems();
+};
+
+/**
+ * Find the data of options that are selected.
*
* @return {Object[]|string[]} Values of selected options
*/
-OO.ui.MultiselectWidget.prototype.getSelectedItemsData = function () {
- return this.getSelectedItems().map( function ( item ) {
+OO.ui.MultiselectWidget.prototype.findSelectedItemsData = function () {
+ return this.findSelectedItems().map( function ( item ) {
return item.data;
} );
};
+/**
+ * Get the data of options that are selected.
+ *
+ * @deprecated Since v0.25.0; use {@link #findSelectedItemsData} instead.
+ * @return {Object[]|string[]} Values of selected options
+ */
+OO.ui.MultiselectWidget.prototype.getSelectedItemsData = function () {
+ OO.ui.warnDeprecation( 'MultiselectWidget#getSelectedItemsData: Deprecated function. Use findSelectedItemsData instead. See T76630.' );
+ return this.findSelectedItemsData();
+};
+
/**
* Select options by reference. Options not mentioned in the `items` array will be deselected.
*
var items,
widget = this;
items = datas.map( function ( data ) {
- return widget.getItemFromData( data );
+ return widget.findItemFromData( data );
} );
this.selectItems( items );
return this;
/**
* CheckboxMultioptionWidget is an option widget that looks like a checkbox.
* The class is used with OO.ui.CheckboxMultiselectWidget to create a selection of checkbox options.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
+ * Please see the [OOUI documentation on MediaWiki] [1] for more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Button_selects_and_option
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Button_selects_and_option
*
* @class
* @extends OO.ui.MultioptionWidget
* CheckboxMultiselectWidget is a {@link OO.ui.MultiselectWidget multiselect widget} that contains
* checkboxes and is used together with OO.ui.CheckboxMultioptionWidget. The
* CheckboxMultiselectWidget provides an interface for adding, removing and selecting options.
- * Please see the [OOjs UI documentation on MediaWiki][1] for more information.
+ * Please see the [OOUI documentation on MediaWiki][1] for more information.
*
* If you want to use this within an HTML form, such as a OO.ui.FormLayout, use
* OO.ui.CheckboxMultiselectInputWidget instead.
*
* $( 'body' ).append( multiselect.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options
*
* @class
* @extends OO.ui.MultiselectWidget
* InputWidget is the base class for all input widgets, which
* include {@link OO.ui.TextInputWidget text inputs}, {@link OO.ui.CheckboxInputWidget checkbox inputs},
* {@link OO.ui.RadioInputWidget radio inputs}, and {@link OO.ui.ButtonInputWidget button inputs}.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ * See the [OOUI documentation on MediaWiki] [1] for more information and examples.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @abstract
* @class
this.value = value;
this.emit( 'change', this.value );
}
+ // The first time that the value is set (probably while constructing the widget),
+ // remember it in defaultValue. This property can be later used to check whether
+ // the value of the input has been changed since it was created.
+ if ( this.defaultValue === undefined ) {
+ this.defaultValue = this.value;
+ this.$input[ 0 ].defaultValue = this.defaultValue;
+ }
return this;
};
* a OO.ui.FormLayout. If you do not need the button to work with HTML forms, you probably
* want to use OO.ui.ButtonWidget instead. Button input widgets can be rendered as either an
* HTML `<button>` (the default) or an HTML `<input>` tags. See the
- * [OOjs UI documentation on MediaWiki] [1] for more information.
+ * [OOUI documentation on MediaWiki] [1] for more information.
*
* @example
* // A ButtonInputWidget rendered as an HTML button, the default.
* } );
* $( 'body' ).append( button.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs#Button_inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs#Button_inputs
*
* @class
* @extends OO.ui.InputWidget
* CheckboxInputWidgets, like HTML checkboxes, can be selected and/or configured with a value.
* Note that these {@link OO.ui.InputWidget input widgets} are best laid out
* in {@link OO.ui.FieldLayout field layouts} that use the {@link OO.ui.FieldLayout#align inline}
- * alignment. For more information, please see the [OOjs UI documentation on MediaWiki][1].
+ * alignment. For more information, please see the [OOUI documentation on MediaWiki][1].
*
* This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* ] );
* $( 'body' ).append( fieldset.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
this.$input.prop( 'checked', this.selected );
this.emit( 'change', this.selected );
}
+ // The first time that the selection state is set (probably while constructing the widget),
+ // remember it in defaultSelected. This property can be later used to check whether
+ // the selection state of the input has been changed since it was created.
+ if ( this.defaultSelected === undefined ) {
+ this.defaultSelected = this.selected;
+ this.$input[ 0 ].defaultChecked = this.defaultSelected;
+ }
return this;
};
/**
* DropdownInputWidget is a {@link OO.ui.DropdownWidget DropdownWidget} intended to be used
* within an HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
- * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * of a hidden HTML `input` tag. Please see the [OOUI documentation on MediaWiki][1] for
* more information about input widgets.
*
* A DropdownInputWidget always has a value (one of the options is always selected), unless there
* } );
* $( 'body' ).append( dropdownInput.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
// Properties (must be done before parent constructor which calls #setDisabled)
this.dropdownWidget = new OO.ui.DropdownWidget( config.dropdown );
+ // Set up the options before parent constructor, which uses them to validate config.value.
+ // Use this instead of setOptions() because this.$input is not set up yet.
+ this.setOptionsData( config.options || [] );
// Parent constructor
OO.ui.DropdownInputWidget.parent.call( this, config );
this.dropdownWidget.getMenu().connect( this, { select: 'onMenuSelect' } );
// Initialization
- this.setOptions( config.options || [] );
- // Set the value again, after we did setOptions(). The call from parent doesn't work because the
- // widget has no valid options when it happens.
- this.setValue( config.value );
this.$element
.addClass( 'oo-ui-dropdownInputWidget' )
.append( this.dropdownWidget.$element );
var selected;
value = this.cleanUpValue( value );
// Only allow setting values that are actually present in the dropdown
- selected = this.dropdownWidget.getMenu().getItemFromData( value ) ||
+ selected = this.dropdownWidget.getMenu().findItemFromData( value ) ||
this.dropdownWidget.getMenu().findFirstSelectableItem();
this.dropdownWidget.getMenu().selectItem( selected );
value = selected ? selected.getData() : '';
OO.ui.DropdownInputWidget.parent.prototype.setValue.call( this, value );
+ if ( this.optionsDirty ) {
+ // We reached this from the constructor or from #setOptions.
+ // We have to update the <select> element.
+ this.updateOptionsInterface();
+ }
return this;
};
* @chainable
*/
OO.ui.DropdownInputWidget.prototype.setOptions = function ( options ) {
+ var value = this.getValue();
+
+ this.setOptionsData( options );
+
+ // Re-set the value to update the visible interface (DropdownWidget and <select>).
+ // In case the previous value is no longer an available option, select the first valid one.
+ this.setValue( value );
+
+ return this;
+};
+
+/**
+ * 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.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @private
+ */
+OO.ui.DropdownInputWidget.prototype.setOptionsData = function ( options ) {
var
- optionWidgets = [],
- value = this.getValue(),
- $optionsContainer = this.$input,
+ optionWidgets,
widget = this;
- this.dropdownWidget.getMenu().clearItems();
- this.$input.empty();
+ this.optionsDirty = true;
- // Rebuild the dropdown menu: our visible one and the hidden `<select>`
- options.forEach( function ( opt ) {
- var optValue, $optionNode, optionWidget;
+ optionWidgets = options.map( function ( opt ) {
+ var optValue, optionWidget;
if ( opt.optgroup === undefined ) {
optValue = widget.cleanUpValue( opt.data );
-
- $optionNode = $( '<option>' )
- .attr( 'value', optValue )
- .text( opt.label !== undefined ? opt.label : optValue );
optionWidget = new OO.ui.MenuOptionWidget( {
data: optValue,
label: opt.label !== undefined ? opt.label : optValue
} );
-
- $optionsContainer.append( $optionNode );
- optionWidgets.push( optionWidget );
} else {
- $optionNode = $( '<optgroup>' )
- .attr( 'label', opt.optgroup );
optionWidget = new OO.ui.MenuSectionOptionWidget( {
label: opt.optgroup
} );
+ }
+
+ return optionWidget;
+ } );
+ this.dropdownWidget.getMenu().clearItems().addItems( optionWidgets );
+};
+
+/**
+ * Update the user-visible interface to match the internal list of options and value.
+ *
+ * This method must only be called after the parent constructor.
+ *
+ * @private
+ */
+OO.ui.DropdownInputWidget.prototype.updateOptionsInterface = function () {
+ var
+ $optionsContainer = this.$input,
+ defaultValue = this.defaultValue,
+ widget = this;
+
+ this.$input.empty();
+
+ this.dropdownWidget.getMenu().getItems().forEach( function ( optionWidget ) {
+ var $optionNode;
+
+ if ( !( optionWidget instanceof OO.ui.MenuSectionOptionWidget ) ) {
+ $optionNode = $( '<option>' )
+ .attr( 'value', optionWidget.getData() )
+ .text( optionWidget.getLabel() );
+
+ // Remember original selection state. This property can be later used to check whether
+ // the selection state of the input has been changed since it was created.
+ $optionNode[ 0 ].defaultSelected = ( optionWidget.getData() === defaultValue );
+
+ $optionsContainer.append( $optionNode );
+ } else {
+ $optionNode = $( '<optgroup>' )
+ .attr( 'label', optionWidget.getLabel() );
widget.$input.append( $optionNode );
$optionsContainer = $optionNode;
- optionWidgets.push( optionWidget );
}
} );
- this.dropdownWidget.getMenu().addItems( optionWidgets );
- // Restore the previous value, or reset to something sensible
- if ( this.dropdownWidget.getMenu().getItemFromData( value ) ) {
- // Previous value is still available, ensure consistency with the dropdown
- this.setValue( value );
- } else {
- // No longer valid, reset
- if ( options.length ) {
- this.setValue( options[ 0 ].data );
- }
- }
-
- return this;
+ this.optionsDirty = false;
};
/**
* RadioInputWidget creates a single radio button. Because radio buttons are usually used as a set,
* in most cases you will want to use a {@link OO.ui.RadioSelectWidget radio select}
* with {@link OO.ui.RadioOptionWidget radio options} instead of this class. For more information,
- * please see the [OOjs UI documentation on MediaWiki][1].
+ * please see the [OOUI documentation on MediaWiki][1].
*
* This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
* ] );
* $( 'body' ).append( fieldset.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
// RadioInputWidget doesn't track its state.
this.$input.prop( 'checked', state );
+ // The first time that the selection state is set (probably while constructing the widget),
+ // remember it in defaultSelected. This property can be later used to check whether
+ // the selection state of the input has been changed since it was created.
+ if ( this.defaultSelected === undefined ) {
+ this.defaultSelected = state;
+ this.$input[ 0 ].defaultChecked = this.defaultSelected;
+ }
return this;
};
/**
* RadioSelectInputWidget is a {@link OO.ui.RadioSelectWidget RadioSelectWidget} intended to be used
* within an HTML form, such as a OO.ui.FormLayout. The selected value is synchronized with the value
- * of a hidden HTML `input` tag. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * 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.
* } );
* $( 'body' ).append( radioSelectInput.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
// Properties (must be done before parent constructor which calls #setDisabled)
this.radioSelectWidget = new OO.ui.RadioSelectWidget();
+ // Set up the options before parent constructor, which uses them to validate config.value.
+ // Use this instead of setOptions() because this.$input is not set up yet
+ this.setOptionsData( config.options || [] );
// Parent constructor
OO.ui.RadioSelectInputWidget.parent.call( this, config );
this.radioSelectWidget.connect( this, { select: 'onMenuSelect' } );
// Initialization
- this.setOptions( config.options || [] );
this.$element
.addClass( 'oo-ui-radioSelectInputWidget' )
.append( this.radioSelectWidget.$element );
* @protected
*/
OO.ui.RadioSelectInputWidget.prototype.getInputElement = function () {
- return $( '<input>' ).attr( 'type', 'hidden' );
+ // Use this instead of <input type="hidden">, because hidden inputs do not have separate
+ // 'value' and 'defaultValue' properties, and InputWidget wants to handle 'defaultValue'.
+ return $( '<input>' ).addClass( 'oo-ui-element-hidden' );
};
/**
* @inheritdoc
*/
OO.ui.RadioSelectInputWidget.prototype.setValue = function ( value ) {
+ var selected;
value = this.cleanUpValue( value );
- this.radioSelectWidget.selectItemByData( value );
+ // Only allow setting values that are actually present in the dropdown
+ selected = this.radioSelectWidget.findItemFromData( value ) ||
+ this.radioSelectWidget.findFirstSelectableItem();
+ this.radioSelectWidget.selectItem( selected );
+ value = selected ? selected.getData() : '';
OO.ui.RadioSelectInputWidget.parent.prototype.setValue.call( this, value );
return this;
};
* @chainable
*/
OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
- var
- value = this.getValue(),
- widget = this;
+ var value = this.getValue();
+
+ this.setOptionsData( options );
+
+ // Re-set the value to update the visible interface (RadioSelectWidget).
+ // In case the previous value is no longer an available option, select the first valid one.
+ this.setValue( value );
+
+ return this;
+};
+
+/**
+ * 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.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @private
+ */
+OO.ui.RadioSelectInputWidget.prototype.setOptionsData = function ( options ) {
+ var widget = this;
- // Rebuild the radioSelect menu
this.radioSelectWidget
.clearItems()
.addItems( options.map( function ( opt ) {
label: opt.label !== undefined ? opt.label : optValue
} );
} ) );
-
- // Restore the previous value, or reset to something sensible
- if ( this.radioSelectWidget.getItemFromData( value ) ) {
- // Previous value is still available, ensure consistency with the radioSelect
- this.setValue( value );
- } else {
- // No longer valid, reset
- if ( options.length ) {
- this.setValue( options[ 0 ].data );
- }
- }
-
- return this;
};
/**
* CheckboxMultiselectInputWidget is a
* {@link OO.ui.CheckboxMultiselectWidget CheckboxMultiselectWidget} intended to be used within a
* HTML form, such as a OO.ui.FormLayout. The selected values are synchronized with the value of
- * HTML `<input type=checkbox>` tags. Please see the [OOjs UI documentation on MediaWiki][1] for
+ * HTML `<input type=checkbox>` tags. Please see the [OOUI documentation on MediaWiki][1] for
* more information about input widgets.
*
* @example
* } );
* $( 'body' ).append( multiselectInput.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
// Properties (must be done before parent constructor which calls #setDisabled)
this.checkboxMultiselectWidget = new OO.ui.CheckboxMultiselectWidget();
+ // Set up the options before parent constructor, which uses them to validate config.value.
+ // Use this instead of setOptions() because this.$input is not set up yet
+ this.setOptionsData( config.options || [] );
// Parent constructor
OO.ui.CheckboxMultiselectInputWidget.parent.call( this, config );
// Properties
this.inputName = config.name;
+ // Events
+ this.checkboxMultiselectWidget.connect( this, { select: 'onCheckboxesSelect' } );
+
// Initialization
this.$element
.addClass( 'oo-ui-checkboxMultiselectInputWidget' )
.append( this.checkboxMultiselectWidget.$element );
// We don't use this.$input, but rather the CheckboxInputWidgets inside each option
this.$input.detach();
- this.setOptions( config.options || [] );
- // Have to repeat this from parent, as we need options to be set up for this to make sense
- this.setValue( config.value );
-
- // setValue when checkboxMultiselectWidget changes
- this.checkboxMultiselectWidget.on( 'change', function () {
- this.setValue( this.checkboxMultiselectWidget.getSelectedItemsData() );
- }.bind( this ) );
};
/* Setup */
return $( '<unused>' );
};
+/**
+ * Handles CheckboxMultiselectWidget select events.
+ *
+ * @private
+ */
+OO.ui.CheckboxMultiselectInputWidget.prototype.onCheckboxesSelect = function () {
+ this.setValue( this.checkboxMultiselectWidget.findSelectedItemsData() );
+};
+
/**
* @inheritdoc
*/
value = this.cleanUpValue( value );
this.checkboxMultiselectWidget.selectItemsByData( value );
OO.ui.CheckboxMultiselectInputWidget.parent.prototype.setValue.call( this, value );
+ if ( this.optionsDirty ) {
+ // We reached this from the constructor or from #setOptions.
+ // We have to update the <select> element.
+ this.updateOptionsInterface();
+ }
return this;
};
singleValue =
OO.ui.CheckboxMultiselectInputWidget.parent.prototype.cleanUpValue.call( this, value[ i ] );
// Remove options that we don't have here
- if ( !this.checkboxMultiselectWidget.getItemFromData( singleValue ) ) {
+ if ( !this.checkboxMultiselectWidget.findItemFromData( singleValue ) ) {
continue;
}
cleanValue.push( singleValue );
* @chainable
*/
OO.ui.CheckboxMultiselectInputWidget.prototype.setOptions = function ( options ) {
+ var value = this.getValue();
+
+ this.setOptionsData( options );
+
+ // Re-set the value to update the visible interface (CheckboxMultiselectWidget).
+ // This will also get rid of any stale options that we just removed.
+ this.setValue( value );
+
+ return this;
+};
+
+/**
+ * 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.
+ *
+ * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
+ * @private
+ */
+OO.ui.CheckboxMultiselectInputWidget.prototype.setOptionsData = function ( options ) {
var widget = this;
- // Rebuild the checkboxMultiselectWidget menu
+ this.optionsDirty = true;
+
this.checkboxMultiselectWidget
.clearItems()
.addItems( options.map( function ( opt ) {
item.checkbox.setValue( optValue );
return item;
} ) );
+};
- // Re-set the value, checking the checkboxes as needed.
- // This will also get rid of any stale options that we just removed.
- this.setValue( this.getValue() );
+/**
+ * Update the user-visible interface to match the internal list of options and value.
+ *
+ * This method must only be called after the parent constructor.
+ *
+ * @private
+ */
+OO.ui.CheckboxMultiselectInputWidget.prototype.updateOptionsInterface = function () {
+ var defaultValue = this.defaultValue;
- return this;
+ this.checkboxMultiselectWidget.getItems().forEach( function ( item ) {
+ // Remember original selection state. This property can be later used to check whether
+ // the selection state of the input has been changed since it was created.
+ var isDefault = defaultValue.indexOf( item.getData() ) !== -1;
+ item.checkbox.defaultSelected = isDefault;
+ item.checkbox.$input[ 0 ].defaultChecked = isDefault;
+ } );
+
+ this.optionsDirty = false;
};
/**
* with {@link OO.ui.mixin.IconElement icons}, {@link OO.ui.mixin.IndicatorElement indicators}, an optional
* validation-pattern (used to determine if an input value is valid or not) and an input filter,
* which modifies incoming values rather than validating them.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ * 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.
*
* } )
* $( 'body' ).append( textInput.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @class
* @extends OO.ui.InputWidget
* instruct the browser to focus this widget.
* @cfg {boolean} [readOnly=false] Prevent changes to the value of the text input.
* @cfg {number} [maxLength] Maximum number of characters allowed in the input.
+ *
+ * For unfortunate historical reasons, this counts the number of UTF-16 code units rather than
+ * Unicode codepoints, which means that codepoints outside the Basic Multilingual Plane (e.g.
+ * many emojis) count as 2 characters each.
* @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
* the value or placeholder text: `'before'` or `'after'`
- * @cfg {boolean} [required=false] Mark the field as required. Implies `indicator: 'required'`.
+ * @cfg {boolean} [required=false] Mark the field as required with `true`. Implies `indicator: 'required'`.
+ * Note that `false` & setting `indicator: 'required' will result in no indicator shown.
* @cfg {boolean} [autocomplete=true] Should the browser support autocomplete for this field
* @cfg {boolean} [spellcheck] Should the browser support spellcheck for this field (`undefined` means
* leaving it up to the browser).
integer: /^\d+$/
};
-/* Static Methods */
-
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
- var state = OO.ui.TextInputWidget.parent.static.gatherPreInfuseState( node, config );
- return state;
-};
-
/* Events */
/**
return this;
};
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.prototype.restorePreInfuseState = function ( state ) {
- OO.ui.TextInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
- if ( state.scrollTop !== undefined ) {
- this.$input.scrollTop( state.scrollTop );
- }
-};
-
/**
* @class
* @extends OO.ui.TextInputWidget
* @param {Object} [config] Configuration options
* @cfg {number} [rows] Number of visible lines in textarea. If used with `autosize`,
* specifies minimum number of rows to display.
- * @cfg {string} [labelPosition='after'] The position of the inline label relative to that of
* @cfg {boolean} [autosize=false] Automatically resize the text input to fit its content.
* Use the #maxRows config to specify a maximum number of displayed rows.
- * @cfg {boolean} [maxRows] Maximum number of rows to display when #autosize is set to true.
+ * @cfg {number} [maxRows] Maximum number of rows to display when #autosize is set to true.
* Defaults to the maximum of `10` and `2 * rows`, or `10` if `rows` isn't provided.
*/
OO.ui.MultilineTextInputWidget = function OoUiMultilineTextInputWidget( config ) {
};
/**
- * Override TextInputWidget so it doesn't emit the 'enter' event.
+ * @inheritdoc
*
- * @private
- * @param {jQuery.Event} e Key press event
+ * Modify to emit 'enter' on Ctrl/Meta+Enter, instead of plain Enter
*/
-OO.ui.MultilineTextInputWidget.prototype.onKeyPress = function () {
- return;
+OO.ui.MultilineTextInputWidget.prototype.onKeyPress = function ( e ) {
+ if (
+ ( e.which === OO.ui.Keys.ENTER && ( e.ctrlKey || e.metaKey ) ) ||
+ // Some platforms emit keycode 10 for ctrl+enter in a textarea
+ e.which === 10
+ ) {
+ this.emit( 'enter', e );
+ }
};
/**
return !!this.autosize;
};
+/**
+ * @inheritdoc
+ */
+OO.ui.MultilineTextInputWidget.prototype.restorePreInfuseState = function ( state ) {
+ OO.ui.MultilineTextInputWidget.parent.prototype.restorePreInfuseState.call( this, state );
+ if ( state.scrollTop !== undefined ) {
+ this.$input.scrollTop( state.scrollTop );
+ }
+};
+
/**
* 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
*
* This widget can be used inside an HTML form, such as a OO.ui.FormLayout.
*
- * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1].
+ * For more information about menus and options, please see the [OOUI documentation on MediaWiki][1].
*
* @example
* // Example: A ComboBoxInputWidget.
* } );
* $( 'body' ).append( comboBox.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options#Menu_selects_and_options
*
* @class
* @extends OO.ui.TextInputWidget
* @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
* containing `<div>` and has a larger area. By default, the menu uses relative positioning.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*/
OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) {
// Configuration initialization
* @param {string} value New value
*/
OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
- var match = this.menu.getItemFromData( value );
+ var match = this.menu.findItemFromData( value );
this.menu.selectItem( match );
if ( this.menu.findHighlightedItem() ) {
* @private
*/
OO.ui.ComboBoxInputWidget.prototype.onMenuItemsChange = function () {
- var match = this.menu.getItemFromData( this.getValue() );
+ var match = this.menu.findItemFromData( this.getValue() );
this.menu.selectItem( match );
if ( this.menu.findHighlightedItem() ) {
this.menu.highlightItem( match );
* An inline-alignment is best used with checkboxes or radio buttons.
*
* Help text is accessed via a help icon that appears in the upper right corner of the rendered field layout.
- * Please see the [OOjs UI documentation on MediaWiki] [1] for examples and more information.
+ * Please see the [OOUI documentation on MediaWiki] [1] for examples and more information.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Layouts/Fields_and_Fieldsets
*
* @class
* @extends OO.ui.Layout
* in the upper-right corner of the rendered field; clicking it will display the text in a popup.
* For important messages, you are advised to use `notices`, as they are always shown.
* @cfg {jQuery} [$overlay] Passed to OO.ui.PopupButtonWidget for help popup, if `help` is given.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*
* @throws {Error} An error is thrown if no widget is specified
*/
} else {
this.$label.on( 'click', function () {
this.fieldWidget.simulateLabelClick();
- return false;
}.bind( this ) );
}
this.$element
}
// Reorder elements
if ( value === 'top' ) {
- this.$header.append( this.$label, this.$help );
+ this.$header.append( this.$help, this.$label );
this.$body.append( this.$header, this.$field );
} else if ( value === 'inline' ) {
- this.$header.append( this.$label, this.$help );
+ this.$header.append( this.$help, this.$label );
this.$body.append( this.$field, this.$header );
} else {
this.$header.append( this.$label );
* FieldsetLayouts are composed of one or more {@link OO.ui.FieldLayout FieldLayouts},
* which each contain an individual widget and, optionally, a label. Each Fieldset can be
* configured with a label as well. For more information and examples,
- * please see the [OOjs UI documentation on MediaWiki][1].
+ * please see the [OOUI documentation on MediaWiki][1].
*
* @example
* // Example of a fieldset layout
* ] );
* $( 'body' ).append( fieldset.$element );
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Fields_and_Fieldsets
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Layouts/Fields_and_Fieldsets
*
* @class
* @extends OO.ui.Layout
* in the upper-right corner of the rendered field; clicking it will display the text in a popup.
* For important messages, you are advised to use `notices`, as they are always shown.
* @cfg {jQuery} [$overlay] Passed to OO.ui.PopupButtonWidget for help popup, if `help` is given.
- * See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
+ * See <https://www.mediawiki.org/wiki/OOUI/Concepts#Overlays>.
*/
OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
// Configuration initialization
* FormLayouts are used to wrap {@link OO.ui.FieldsetLayout FieldsetLayouts} when you intend to use browser-based
* form submission for the fields instead of handling them in JavaScript. Form layouts can be configured with an
* HTML form action, an encoding type, and a method using the #action, #enctype, and #method configs, respectively.
- * See the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+ * See the [OOUI documentation on MediaWiki] [1] for more information and examples.
*
* Only widgets from the {@link OO.ui.InputWidget InputWidget} family support form submission. It
* includes standard form elements like {@link OO.ui.CheckboxInputWidget checkboxes}, {@link
* some fancier controls. Some controls have both regular and InputWidget variants, for example
* OO.ui.DropdownWidget and OO.ui.DropdownInputWidget – only the latter support form submission and
* often have simplified APIs to match the capabilities of HTML forms.
- * See the [OOjs UI Inputs documentation on MediaWiki] [2] for more information about InputWidgets.
+ * See the [OOUI documentation on MediaWiki] [2] for more information about InputWidgets.
*
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Layouts/Forms
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Layouts/Forms
+ * [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Inputs
*
* @example
* // Example of a form layout that wraps a fieldset layout