Merge "Revert "Log the reason why revision->getContent() returns null""
[lhc/web/wiklou.git] / resources / lib / oojs-ui / oojs-ui-core.js
index 64d1476..12e5ef6 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * 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 ) {
 
@@ -68,7 +68,7 @@ OO.ui.elementId = 0;
  */
 OO.ui.generateElementId = function () {
        OO.ui.elementId++;
-       return 'oojsui-' + OO.ui.elementId;
+       return 'ooui-' + OO.ui.elementId;
 };
 
 /**
@@ -411,7 +411,7 @@ OO.ui.infuse = function ( idOrNode ) {
         *
         *     $.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;
@@ -558,7 +558,7 @@ OO.ui.getViewportSpacing = function () {
 
 /**
  * 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
  */
@@ -575,7 +575,7 @@ OO.ui.getDefaultOverlay = function () {
  */
 
 /**
- * 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
@@ -598,9 +598,9 @@ OO.ui.mixin = {};
  * @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).
@@ -826,16 +826,16 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
        // 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();
        }
@@ -1634,7 +1634,7 @@ OO.inheritClass( OO.ui.Layout, OO.ui.Element );
 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.
  *
@@ -2083,9 +2083,9 @@ OO.ui.mixin.TabIndexedElement.prototype.simulateLabelClick = function () {
 /**
  * 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
@@ -2340,12 +2340,12 @@ OO.ui.mixin.ButtonElement.prototype.isActive = function () {
 };
 
 /**
- * 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
@@ -2403,15 +2403,15 @@ OO.ui.mixin.GroupElement.prototype.setGroupElement = function ( $group ) {
 };
 
 /**
- * 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 );
 
@@ -2426,14 +2426,26 @@ OO.ui.mixin.GroupElement.prototype.getItemFromData = function ( 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 = [];
@@ -2448,6 +2460,18 @@ OO.ui.mixin.GroupElement.prototype.getItemsFromData = function ( data ) {
        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.
  *
@@ -2566,10 +2590,10 @@ OO.ui.mixin.GroupElement.prototype.clearItems = function () {
  * 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
@@ -2595,8 +2619,8 @@ OO.ui.mixin.GroupElement.prototype.clearItems = function () {
  *  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.
  */
@@ -2762,9 +2786,9 @@ OO.ui.mixin.IconElement.prototype.getIconTitle = function () {
  *   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
@@ -2773,10 +2797,10 @@ OO.ui.mixin.IconElement.prototype.getIconTitle = function () {
  * @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.
@@ -2803,7 +2827,7 @@ OO.initClass( OO.ui.mixin.IndicatorElement );
 /* 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
@@ -2849,7 +2873,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicatorElement = function ( $indicat
 };
 
 /**
- * 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
@@ -2904,7 +2928,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorT
 };
 
 /**
- * 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
  */
@@ -2926,9 +2950,9 @@ OO.ui.mixin.IndicatorElement.prototype.getIndicatorTitle = function () {
 /**
  * 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
@@ -2939,8 +2963,8 @@ OO.ui.mixin.IndicatorElement.prototype.getIndicatorTitle = function () {
  *  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
@@ -3125,7 +3149,6 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  *
  * - **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:
  *
@@ -3142,9 +3165,9 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  *     $( '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
@@ -3152,8 +3175,8 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  * @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.
@@ -3593,10 +3616,10 @@ OO.ui.mixin.AccessKeyedElement.prototype.formatTitleWithAccessKey = function ( t
 /**
  * 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
@@ -3901,7 +3924,7 @@ OO.ui.ButtonGroupWidget.prototype.simulateLabelClick = function () {
 
 /**
  * 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
@@ -3916,7 +3939,7 @@ OO.ui.ButtonGroupWidget.prototype.simulateLabelClick = function () {
  *      } );
  *      $( '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
@@ -3960,23 +3983,23 @@ OO.ui.IconWidget.static.tagName = 'span';
 
 /**
  * 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
@@ -4075,7 +4098,6 @@ OO.ui.LabelWidget = function OoUiLabelWidget( config ) {
                } else {
                        this.$label.on( 'click', function () {
                                this.input.simulateLabelClick();
-                               return false;
                        }.bind( this ) );
                }
        }
@@ -4272,6 +4294,7 @@ OO.ui.mixin.FloatableElement = function OoUiMixinFloatableElement( config ) {
        this.$floatableContainer = null;
        this.$floatableWindow = null;
        this.$floatableClosestScrollable = null;
+       this.floatableOutOfView = false;
        this.onFloatableScrollHandler = this.position.bind( this );
        this.onFloatableWindowResizeHandler = this.position.bind( this );
 
@@ -4475,6 +4498,15 @@ OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function ( $element
                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.
  *
@@ -4501,7 +4533,8 @@ OO.ui.mixin.FloatableElement.prototype.position = function () {
                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 {
@@ -5036,7 +5069,7 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () {
 /**
  * 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.
  *
@@ -5052,7 +5085,7 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () {
  *     // 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
@@ -5089,16 +5122,16 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () {
  *  '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
@@ -5375,13 +5408,13 @@ OO.ui.PopupWidget.prototype.toggle = function ( show ) {
 
                        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();
@@ -5393,7 +5426,7 @@ OO.ui.PopupWidget.prototype.toggle = function ( show ) {
                                        }
                                }
                                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();
@@ -5403,7 +5436,7 @@ OO.ui.PopupWidget.prototype.toggle = function ( show ) {
                                                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();
@@ -5790,7 +5823,7 @@ OO.ui.mixin.PopupElement.prototype.getPopup = function () {
  * @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
@@ -5937,9 +5970,9 @@ OO.ui.mixin.ItemWidget.prototype.setElementGroup = function ( group ) {
  * 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
@@ -6034,7 +6067,7 @@ OO.ui.OptionWidget.static.scrollIntoViewOnSelect = false;
  * @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();
 };
 
 /**
@@ -6045,7 +6078,7 @@ OO.ui.OptionWidget.prototype.isSelectable = function () {
  * @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();
 };
 
 /**
@@ -6055,7 +6088,7 @@ OO.ui.OptionWidget.prototype.isHighlightable = function () {
  * @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();
 };
 
 /**
@@ -6160,13 +6193,13 @@ OO.ui.OptionWidget.prototype.getMatchText = function () {
 };
 
 /**
- * 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
@@ -6188,7 +6221,7 @@ OO.ui.OptionWidget.prototype.getMatchText = function () {
  *     } );
  *     $( '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
@@ -6199,8 +6232,8 @@ OO.ui.OptionWidget.prototype.getMatchText = function () {
  * @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
@@ -6313,7 +6346,7 @@ OO.ui.SelectWidget.prototype.onFocus = function ( event ) {
        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 {
@@ -6450,7 +6483,7 @@ OO.ui.SelectWidget.prototype.onMouseLeave = function () {
 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 ) {
@@ -6576,7 +6609,7 @@ OO.ui.SelectWidget.prototype.onKeyPress = function ( e ) {
        }
        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
@@ -6684,11 +6717,11 @@ OO.ui.SelectWidget.prototype.findTargetItem = function ( e ) {
 };
 
 /**
- * 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++ ) {
@@ -6699,6 +6732,17 @@ OO.ui.SelectWidget.prototype.getSelectedItem = function () {
        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.
  *
@@ -6715,17 +6759,6 @@ OO.ui.SelectWidget.prototype.findHighlightedItem = function () {
        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.
  *
@@ -6843,7 +6876,7 @@ OO.ui.SelectWidget.prototype.selectItemByLabel = function ( label, prefix ) {
  * @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();
        }
@@ -6974,24 +7007,6 @@ OO.ui.SelectWidget.prototype.findRelativeSelectableItem = function ( item, direc
        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.
@@ -7002,18 +7017,6 @@ OO.ui.SelectWidget.prototype.findFirstSelectableItem = function () {
        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.
@@ -7100,7 +7103,7 @@ OO.ui.SelectWidget.prototype.setFocusOwner = function ( $focusOwner ) {
  * 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
@@ -7120,7 +7123,7 @@ OO.ui.SelectWidget.prototype.setFocusOwner = function ( $focusOwner ) {
  *     } );
  *     $( '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
@@ -7154,9 +7157,9 @@ OO.mixinClass( OO.ui.DecoratedOptionWidget, OO.ui.mixin.IndicatorElement );
 /**
  * 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
@@ -7268,8 +7271,8 @@ OO.ui.MenuSectionOptionWidget.static.highlightable = false;
  *
  * 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
@@ -7369,7 +7372,7 @@ OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
  * @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 ) {
@@ -7607,11 +7610,11 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
                        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();
@@ -7625,9 +7628,9 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
 
                        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
@@ -7684,11 +7687,11 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
  *
  *     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
@@ -7704,7 +7707,7 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
  * @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
@@ -7863,9 +7866,9 @@ OO.ui.DropdownWidget.prototype.onKeyDown = function ( e ) {
 /**
  * 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
@@ -7955,7 +7958,7 @@ OO.ui.RadioOptionWidget.prototype.setDisabled = function ( disabled ) {
  * 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.
@@ -7981,7 +7984,7 @@ OO.ui.RadioOptionWidget.prototype.setDisabled = function ( disabled ) {
  *
  *     $( '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
@@ -8019,9 +8022,9 @@ OO.mixinClass( OO.ui.RadioSelectWidget, OO.ui.mixin.TabIndexedElement );
  * 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
@@ -8101,9 +8104,9 @@ OO.ui.MultioptionWidget.prototype.setSelected = function ( state ) {
 /**
  * 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
@@ -8161,27 +8164,49 @@ OO.mixinClass( OO.ui.MultiselectWidget, OO.ui.mixin.GroupWidget );
 /* 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.
  *
@@ -8206,7 +8231,7 @@ OO.ui.MultiselectWidget.prototype.selectItemsByData = function ( datas ) {
        var items,
                widget = this;
        items = datas.map( function ( data ) {
-               return widget.getItemFromData( data );
+               return widget.findItemFromData( data );
        } );
        this.selectItems( items );
        return this;
@@ -8215,9 +8240,9 @@ OO.ui.MultiselectWidget.prototype.selectItemsByData = function ( datas ) {
 /**
  * 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
@@ -8320,7 +8345,7 @@ OO.ui.CheckboxMultioptionWidget.prototype.onKeyDown = function ( e ) {
  * 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.
@@ -8344,7 +8369,7 @@ OO.ui.CheckboxMultioptionWidget.prototype.onKeyDown = function ( e ) {
  *
  *     $( '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
@@ -8582,9 +8607,9 @@ OO.ui.ProgressBarWidget.prototype.setProgress = function ( progress ) {
  * 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
@@ -8761,6 +8786,13 @@ OO.ui.InputWidget.prototype.setValue = function ( value ) {
                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;
 };
 
@@ -8862,7 +8894,7 @@ OO.ui.HiddenInputWidget.static.tagName = 'input';
  * 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.
@@ -8873,7 +8905,7 @@ OO.ui.HiddenInputWidget.static.tagName = 'input';
  *     } );
  *     $( '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
@@ -9004,7 +9036,7 @@ OO.ui.ButtonInputWidget.prototype.getInputId = function () {
  * 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.
  *
@@ -9032,7 +9064,7 @@ OO.ui.ButtonInputWidget.prototype.getInputId = function () {
  *     ] );
  *     $( '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
@@ -9115,6 +9147,13 @@ OO.ui.CheckboxInputWidget.prototype.setSelected = function ( state ) {
                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;
 };
 
@@ -9156,7 +9195,7 @@ OO.ui.CheckboxInputWidget.prototype.restorePreInfuseState = function ( state ) {
 /**
  * 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
@@ -9176,7 +9215,7 @@ OO.ui.CheckboxInputWidget.prototype.restorePreInfuseState = function ( state ) {
  *     } );
  *     $( '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
@@ -9192,6 +9231,9 @@ OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) {
 
        // 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 );
@@ -9200,10 +9242,6 @@ OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( 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 );
@@ -9241,11 +9279,16 @@ OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) {
        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;
 };
 
@@ -9265,58 +9308,91 @@ OO.ui.DropdownInputWidget.prototype.setDisabled = function ( state ) {
  * @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;
 };
 
 /**
@@ -9339,7 +9415,7 @@ OO.ui.DropdownInputWidget.prototype.blur = function () {
  * 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.
  *
@@ -9367,7 +9443,7 @@ OO.ui.DropdownInputWidget.prototype.blur = function () {
  *     ] );
  *     $( '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
@@ -9440,6 +9516,13 @@ OO.ui.RadioInputWidget.prototype.onEdit = function () {
 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;
 };
 
@@ -9475,7 +9558,7 @@ OO.ui.RadioInputWidget.prototype.restorePreInfuseState = function ( state ) {
 /**
  * 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.
@@ -9491,7 +9574,7 @@ OO.ui.RadioInputWidget.prototype.restorePreInfuseState = function ( state ) {
  *     } );
  *     $( '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
@@ -9506,6 +9589,9 @@ OO.ui.RadioSelectInputWidget = function OoUiRadioSelectInputWidget( config ) {
 
        // 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 );
@@ -9514,7 +9600,6 @@ OO.ui.RadioSelectInputWidget = function OoUiRadioSelectInputWidget( config ) {
        this.radioSelectWidget.connect( this, { select: 'onMenuSelect' } );
 
        // Initialization
-       this.setOptions( config.options || [] );
        this.$element
                .addClass( 'oo-ui-radioSelectInputWidget' )
                .append( this.radioSelectWidget.$element );
@@ -9553,7 +9638,9 @@ OO.ui.RadioSelectInputWidget.static.reusePreInfuseDOM = function ( node, config
  * @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' );
 };
 
 /**
@@ -9570,8 +9657,13 @@ OO.ui.RadioSelectInputWidget.prototype.onMenuSelect = function ( item ) {
  * @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;
 };
@@ -9592,11 +9684,29 @@ OO.ui.RadioSelectInputWidget.prototype.setDisabled = function ( state ) {
  * @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 ) {
@@ -9606,19 +9716,6 @@ OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
                                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;
 };
 
 /**
@@ -9641,7 +9738,7 @@ OO.ui.RadioSelectInputWidget.prototype.blur = function () {
  * 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
@@ -9655,7 +9752,7 @@ OO.ui.RadioSelectInputWidget.prototype.blur = function () {
  *     } );
  *     $( '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
@@ -9670,6 +9767,9 @@ OO.ui.CheckboxMultiselectInputWidget = function OoUiCheckboxMultiselectInputWidg
 
        // 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 );
@@ -9677,20 +9777,15 @@ OO.ui.CheckboxMultiselectInputWidget = function OoUiCheckboxMultiselectInputWidg
        // 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 */
@@ -9730,6 +9825,15 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.getInputElement = function () {
        return $( '<unused>' );
 };
 
+/**
+ * Handles CheckboxMultiselectWidget select events.
+ *
+ * @private
+ */
+OO.ui.CheckboxMultiselectInputWidget.prototype.onCheckboxesSelect = function () {
+       this.setValue( this.checkboxMultiselectWidget.findSelectedItemsData() );
+};
+
 /**
  * @inheritdoc
  */
@@ -9749,6 +9853,11 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.setValue = function ( value ) {
        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;
 };
 
@@ -9768,7 +9877,7 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.cleanUpValue = function ( value )
                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 );
@@ -9792,9 +9901,31 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.setDisabled = function ( state )
  * @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 ) {
@@ -9812,12 +9943,27 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.setOptions = function ( options )
                        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;
 };
 
 /**
@@ -9834,7 +9980,7 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.focus = function () {
  * 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.
  *
@@ -9845,7 +9991,7 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.focus = function () {
  *     } )
  *     $( '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
@@ -9863,9 +10009,14 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.focus = function () {
  *  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).
@@ -9972,16 +10123,6 @@ OO.ui.TextInputWidget.static.validationPatterns = {
        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 */
 
 /**
@@ -10529,16 +10670,6 @@ OO.ui.TextInputWidget.prototype.positionLabel = function () {
        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
@@ -10641,10 +10772,9 @@ OO.ui.SearchInputWidget.prototype.setReadOnly = function ( state ) {
  * @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 ) {
@@ -10728,13 +10858,18 @@ OO.ui.MultilineTextInputWidget.prototype.updatePosition = function () {
 };
 
 /**
- * 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 );
+       }
 };
 
 /**
@@ -10838,6 +10973,16 @@ OO.ui.MultilineTextInputWidget.prototype.isAutosizing = function () {
        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
@@ -10854,7 +10999,7 @@ OO.ui.MultilineTextInputWidget.prototype.isAutosizing = function () {
  *
  * 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.
@@ -10889,7 +11034,7 @@ OO.ui.MultilineTextInputWidget.prototype.isAutosizing = function () {
  *     } );
  *     $( '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
@@ -10901,7 +11046,7 @@ OO.ui.MultilineTextInputWidget.prototype.isAutosizing = function () {
  * @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
@@ -11003,7 +11148,7 @@ OO.ui.ComboBoxInputWidget.prototype.getInput = function () {
  * @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() ) {
@@ -11052,7 +11197,7 @@ OO.ui.ComboBoxInputWidget.prototype.onMenuChoose = function ( item ) {
  * @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 );
@@ -11123,9 +11268,9 @@ OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
  *   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
@@ -11144,7 +11289,7 @@ OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
  *  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
  */
@@ -11222,7 +11367,6 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
        } else {
                this.$label.on( 'click', function () {
                        this.fieldWidget.simulateLabelClick();
-                       return false;
                }.bind( this ) );
        }
        this.$element
@@ -11324,10 +11468,10 @@ OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
                }
                // 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 );
@@ -11496,7 +11640,7 @@ OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
  * 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
@@ -11522,7 +11666,7 @@ OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
  *     ] );
  *     $( '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
@@ -11537,7 +11681,7 @@ OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout );
  *  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
@@ -11605,7 +11749,7 @@ OO.ui.FieldsetLayout.static.tagName = 'fieldset';
  * 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
@@ -11613,10 +11757,10 @@ OO.ui.FieldsetLayout.static.tagName = 'fieldset';
  * 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