Update OOUI to v0.29.4
[lhc/web/wiklou.git] / resources / lib / ooui / oojs-ui-core.js
index e967977..f41d24b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.29.2
+ * OOUI v0.29.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2018 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2018-10-08T22:42:55Z
+ * Date: 2018-11-07T00:58:30Z
  */
 ( function ( OO ) {
 
@@ -384,7 +384,9 @@ OO.ui.infuse = function ( idOrNode, config ) {
                // Label for the file selection widget when no file is currently selected
                'ooui-selectfile-placeholder': 'No file is selected',
                // Label for the file selection widget's drop target
-               'ooui-selectfile-dragdrop-placeholder': 'Drop file here'
+               'ooui-selectfile-dragdrop-placeholder': 'Drop file here',
+               // Label for the help icon attached to a form field
+               'ooui-field-help': 'Help'
        };
 
        /**
@@ -1377,6 +1379,7 @@ OO.ui.Element.static.reconsiderScrollbars = function ( el ) {
  * @param {boolean} [show] Make element visible, omit to toggle visibility
  * @fires visible
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.Element.prototype.toggle = function ( show ) {
        show = show === undefined ? !this.visible : !!show;
@@ -1413,6 +1416,7 @@ OO.ui.Element.prototype.getData = function () {
  *
  * @param {Mixed} data Element data
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.Element.prototype.setData = function ( data ) {
        this.data = data;
@@ -1424,6 +1428,7 @@ OO.ui.Element.prototype.setData = function ( data ) {
  *
  * @param {string} id
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.Element.prototype.setElementId = function ( id ) {
        this.elementId = id;
@@ -1537,6 +1542,7 @@ OO.ui.Element.prototype.getElementGroup = function () {
  *
  * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.Element.prototype.setElementGroup = function ( group ) {
        this.elementGroup = group;
@@ -1636,6 +1642,21 @@ OO.ui.Layout = function OoUiLayout( config ) {
 OO.inheritClass( OO.ui.Layout, OO.ui.Element );
 OO.mixinClass( OO.ui.Layout, OO.EventEmitter );
 
+/* Methods */
+
+/**
+ * Reset scroll offsets
+ *
+ * @chainable
+ * @return {OO.ui.Layout} The layout, for chaining
+ */
+OO.ui.Layout.prototype.resetScroll = function () {
+       this.$element[ 0 ].scrollTop = 0;
+       // TODO: Reset scrollLeft in an RTL-aware manner, see OO.ui.Element.static.getScrollLeft.
+
+       return this;
+};
+
 /**
  * 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,
@@ -1712,6 +1733,7 @@ OO.ui.Widget.prototype.isDisabled = function () {
  *
  * @param {boolean} disabled Disable widget
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
        var isDisabled;
@@ -1734,6 +1756,7 @@ OO.ui.Widget.prototype.setDisabled = function ( disabled ) {
  * Update the disabled state, in case of changes in parent widget.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.Widget.prototype.updateDisabled = function () {
        this.setDisabled( this.disabled );
@@ -1933,6 +1956,7 @@ OO.initClass( OO.ui.mixin.TabIndexedElement );
  *
  * @param {jQuery} $tabIndexed Element that should use the tabindex functionality
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIndexed ) {
        var tabIndex = this.tabIndex;
@@ -1949,6 +1973,7 @@ OO.ui.mixin.TabIndexedElement.prototype.setTabIndexedElement = function ( $tabIn
  *
  * @param {string|number|null} tabIndex Tabindex value, or `null` for no tabindex
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
        tabIndex = /^-?\d+$/.test( tabIndex ) ? Number( tabIndex ) : null;
@@ -1967,6 +1992,7 @@ OO.ui.mixin.TabIndexedElement.prototype.setTabIndex = function ( tabIndex ) {
  *
  * @private
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TabIndexedElement.prototype.updateTabIndex = function () {
        if ( this.$tabIndexed ) {
@@ -2058,6 +2084,7 @@ OO.ui.mixin.TabIndexedElement.prototype.isLabelableNode = function ( $node ) {
  * Focus this element.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TabIndexedElement.prototype.focus = function () {
        if ( !this.isDisabled() ) {
@@ -2070,6 +2097,7 @@ OO.ui.mixin.TabIndexedElement.prototype.focus = function () {
  * Blur this element.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TabIndexedElement.prototype.blur = function () {
        this.$tabIndexed.blur();
@@ -2182,7 +2210,7 @@ OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
                } );
 
        // Add `role="button"` on `<a>` elements, where it's needed
-       // `toUppercase()` is added for XHTML documents
+       // `toUpperCase()` is added for XHTML documents
        if ( this.$button.prop( 'tagName' ).toUpperCase() === 'A' ) {
                this.$button.attr( 'role', 'button' );
        }
@@ -2193,6 +2221,7 @@ OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
  *
  * @protected
  * @param {jQuery.Event} e Mouse down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
        if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
@@ -2235,6 +2264,7 @@ OO.ui.mixin.ButtonElement.prototype.onMouseUp = function () {
  * @protected
  * @param {jQuery.Event} e Mouse click event
  * @fires click
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
        if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
@@ -2287,6 +2317,7 @@ OO.ui.mixin.ButtonElement.prototype.onKeyUp = function () {
  * @protected
  * @param {jQuery.Event} e Key press event
  * @fires click
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onKeyPress = function ( e ) {
        if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
@@ -2310,6 +2341,7 @@ OO.ui.mixin.ButtonElement.prototype.isFramed = function () {
  *
  * @param {boolean} [framed] Make button framed, omit to toggle
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
        framed = framed === undefined ? !this.framed : !!framed;
@@ -2336,6 +2368,7 @@ OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
  * @protected
  * @param {boolean} value Make button active
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
        this.active = !!value;
@@ -2472,6 +2505,7 @@ OO.ui.mixin.GroupElement.prototype.findItemsFromData = function ( data ) {
  * @param {OO.ui.Element[]} items An array of items to add to the group
  * @param {number} [index] Index of the insertion point
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.GroupElement.prototype.addItems = function ( items, index ) {
        // Mixin method
@@ -2532,6 +2566,7 @@ OO.ui.mixin.GroupElement.prototype.insertItemElements = function ( itemWidget, i
  *
  * @param {OO.ui.Element[]} items An array of items to remove
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
        var i, len, item, index;
@@ -2560,6 +2595,7 @@ OO.ui.mixin.GroupElement.prototype.removeItems = function ( items ) {
  * To remove only a subset of items from a group, use the #removeItems method.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.GroupElement.prototype.clearItems = function () {
        var i, len;
@@ -2577,6 +2613,225 @@ OO.ui.mixin.GroupElement.prototype.clearItems = function () {
        return this;
 };
 
+/**
+ * LabelElement is often mixed into other classes to generate a label, which
+ * helps identify the function of an interface element.
+ * See the [OOUI documentation on MediaWiki] [1] for more information.
+ *
+ * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$label] The label element created by the class. If this
+ *  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 [OOUI documentation on MediaWiki] [2] for examples.
+ *  [2]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Labels
+ * @cfg {boolean} [invisibleLabel] Whether the label should be visually hidden (but still accessible
+ *  to screen-readers).
+ */
+OO.ui.mixin.LabelElement = function OoUiMixinLabelElement( config ) {
+       // Configuration initialization
+       config = config || {};
+
+       // Properties
+       this.$label = null;
+       this.label = null;
+       this.invisibleLabel = null;
+
+       // Initialization
+       this.setLabel( config.label || this.constructor.static.label );
+       this.setLabelElement( config.$label || $( '<span>' ) );
+       this.setInvisibleLabel( config.invisibleLabel );
+};
+
+/* Setup */
+
+OO.initClass( OO.ui.mixin.LabelElement );
+
+/* Events */
+
+/**
+ * @event labelChange
+ * @param {string} value
+ */
+
+/* Static Properties */
+
+/**
+ * The label text. The label can be specified as a plaintext string, a function that will
+ * produce a string in the future, or `null` for no label. The static value will
+ * be overridden if a label is specified with the #label config option.
+ *
+ * @static
+ * @inheritable
+ * @property {string|Function|null}
+ */
+OO.ui.mixin.LabelElement.static.label = null;
+
+/* Static methods */
+
+/**
+ * Highlight the first occurrence of the query in the given text
+ *
+ * @param {string} text Text
+ * @param {string} query Query to find
+ * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
+ * @return {jQuery} Text with the first match of the query
+ *  sub-string wrapped in highlighted span
+ */
+OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query, compare ) {
+       var i, tLen, qLen,
+               offset = -1,
+               $result = $( '<span>' );
+
+       if ( compare ) {
+               tLen = text.length;
+               qLen = query.length;
+               for ( i = 0; offset === -1 && i <= tLen - qLen; i++ ) {
+                       if ( compare( query, text.slice( i, i + qLen ) ) === 0 ) {
+                               offset = i;
+                       }
+               }
+       } else {
+               offset = text.toLowerCase().indexOf( query.toLowerCase() );
+       }
+
+       if ( !query.length || offset === -1 ) {
+               $result.text( text );
+       } else {
+               $result.append(
+                       document.createTextNode( text.slice( 0, offset ) ),
+                       $( '<span>' )
+                               .addClass( 'oo-ui-labelElement-label-highlight' )
+                               .text( text.slice( offset, offset + query.length ) ),
+                       document.createTextNode( text.slice( offset + query.length ) )
+               );
+       }
+       return $result.contents();
+};
+
+/* Methods */
+
+/**
+ * Set the label element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the new element.
+ *
+ * @param {jQuery} $label Element to use as label
+ */
+OO.ui.mixin.LabelElement.prototype.setLabelElement = function ( $label ) {
+       if ( this.$label ) {
+               this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
+       }
+
+       this.$label = $label.addClass( 'oo-ui-labelElement-label' );
+       this.setLabelContent( this.label );
+};
+
+/**
+ * Set the label.
+ *
+ * An empty string will result in the label being hidden. A string containing only whitespace will
+ * be converted to a single `&nbsp;`.
+ *
+ * @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ * @chainable
+ * @return {OO.ui.Element} The element, for chaining
+ */
+OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
+       label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
+       label = ( ( typeof label === 'string' || label instanceof jQuery ) && label.length ) || ( label instanceof OO.ui.HtmlSnippet && label.toString().length ) ? label : null;
+
+       if ( this.label !== label ) {
+               if ( this.$label ) {
+                       this.setLabelContent( label );
+               }
+               this.label = label;
+               this.emit( 'labelChange' );
+       }
+
+       this.$element.toggleClass( 'oo-ui-labelElement', !!this.label && !this.invisibleLabel );
+
+       return this;
+};
+
+/**
+ * Set whether the label should be visually hidden (but still accessible to screen-readers).
+ *
+ * @param {boolean} invisibleLabel
+ * @chainable
+ * @return {OO.ui.Element} The element, for chaining
+ */
+OO.ui.mixin.LabelElement.prototype.setInvisibleLabel = function ( invisibleLabel ) {
+       invisibleLabel = !!invisibleLabel;
+
+       if ( this.invisibleLabel !== invisibleLabel ) {
+               this.invisibleLabel = invisibleLabel;
+               this.emit( 'labelChange' );
+       }
+
+       this.$label.toggleClass( 'oo-ui-labelElement-invisible', this.invisibleLabel );
+       // Pretend that there is no label, a lot of CSS has been written with this assumption
+       this.$element.toggleClass( 'oo-ui-labelElement', !!this.label && !this.invisibleLabel );
+
+       return this;
+};
+
+/**
+ * Set the label as plain text with a highlighted query
+ *
+ * @param {string} text Text label to set
+ * @param {string} query Substring of text to highlight
+ * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
+ * @chainable
+ * @return {OO.ui.Element} The element, for chaining
+ */
+OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query, compare ) {
+       return this.setLabel( this.constructor.static.highlightQuery( text, query, compare ) );
+};
+
+/**
+ * Get the label.
+ *
+ * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ */
+OO.ui.mixin.LabelElement.prototype.getLabel = function () {
+       return this.label;
+};
+
+/**
+ * Set the content of the label.
+ *
+ * Do not call this method until after the label element has been set by #setLabelElement.
+ *
+ * @private
+ * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
+ *  text; or null for no label
+ */
+OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
+       if ( typeof label === 'string' ) {
+               if ( label.match( /^\s*$/ ) ) {
+                       // Convert whitespace only string to a single non-breaking space
+                       this.$label.html( '&nbsp;' );
+               } else {
+                       this.$label.text( label );
+               }
+       } else if ( label instanceof OO.ui.HtmlSnippet ) {
+               this.$label.html( label.toString() );
+       } else if ( label instanceof jQuery ) {
+               this.$label.empty().append( label );
+       } else {
+               this.$label.empty();
+       }
+};
+
 /**
  * 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
@@ -2701,6 +2956,7 @@ OO.ui.mixin.IconElement.prototype.setIconElement = function ( $icon ) {
  * @param {Object|string|null} icon A symbolic icon name, a {@link #icon map of icon names} keyed
  *  by language code, or `null` to remove the icon.
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.IconElement.prototype.setIcon = function ( icon ) {
        icon = OO.isPlainObject( icon ) ? OO.ui.getLocalValue( icon, null, 'default' ) : icon;
@@ -2733,6 +2989,7 @@ OO.ui.mixin.IconElement.prototype.setIcon = function ( icon ) {
  * @param {string|Function|null} iconTitle A text string used as the icon title,
  *  a function that returns title text, or `null` for no title.
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.IconElement.prototype.setIconTitle = function ( iconTitle ) {
        iconTitle =
@@ -2873,6 +3130,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicatorElement = function ( $indicat
  *
  * @param {string|null} indicator Symbolic name of indicator, or `null` for no indicator
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) {
        indicator = typeof indicator === 'string' && indicator.length ? indicator.trim() : null;
@@ -2906,6 +3164,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) {
  * @param {string|Function|null} indicatorTitle Indicator title text, a function that returns text, or
  *   `null` for no indicator title
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle ) {
        indicatorTitle =
@@ -2946,197 +3205,6 @@ OO.ui.mixin.IndicatorElement.prototype.getIndicatorTitle = function () {
        return this.indicatorTitle;
 };
 
-/**
- * LabelElement is often mixed into other classes to generate a label, which
- * helps identify the function of an interface element.
- * See the [OOUI documentation on MediaWiki] [1] for more information.
- *
- * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Icons,_Indicators,_and_Labels#Labels
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$label] The label element created by the class. If this
- *  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 [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
-       config = config || {};
-
-       // Properties
-       this.$label = null;
-       this.label = null;
-
-       // Initialization
-       this.setLabel( config.label || this.constructor.static.label );
-       this.setLabelElement( config.$label || $( '<span>' ) );
-};
-
-/* Setup */
-
-OO.initClass( OO.ui.mixin.LabelElement );
-
-/* Events */
-
-/**
- * @event labelChange
- * @param {string} value
- */
-
-/* Static Properties */
-
-/**
- * The label text. The label can be specified as a plaintext string, a function that will
- * produce a string in the future, or `null` for no label. The static value will
- * be overridden if a label is specified with the #label config option.
- *
- * @static
- * @inheritable
- * @property {string|Function|null}
- */
-OO.ui.mixin.LabelElement.static.label = null;
-
-/* Static methods */
-
-/**
- * Highlight the first occurrence of the query in the given text
- *
- * @param {string} text Text
- * @param {string} query Query to find
- * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
- * @return {jQuery} Text with the first match of the query
- *  sub-string wrapped in highlighted span
- */
-OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query, compare ) {
-       var i, tLen, qLen,
-               offset = -1,
-               $result = $( '<span>' );
-
-       if ( compare ) {
-               tLen = text.length;
-               qLen = query.length;
-               for ( i = 0; offset === -1 && i <= tLen - qLen; i++ ) {
-                       if ( compare( query, text.slice( i, i + qLen ) ) === 0 ) {
-                               offset = i;
-                       }
-               }
-       } else {
-               offset = text.toLowerCase().indexOf( query.toLowerCase() );
-       }
-
-       if ( !query.length || offset === -1 ) {
-               $result.text( text );
-       } else {
-               $result.append(
-                       document.createTextNode( text.slice( 0, offset ) ),
-                       $( '<span>' )
-                               .addClass( 'oo-ui-labelElement-label-highlight' )
-                               .text( text.slice( offset, offset + query.length ) ),
-                       document.createTextNode( text.slice( offset + query.length ) )
-               );
-       }
-       return $result.contents();
-};
-
-/* Methods */
-
-/**
- * Set the label element.
- *
- * If an element is already set, it will be cleaned up before setting up the new element.
- *
- * @param {jQuery} $label Element to use as label
- */
-OO.ui.mixin.LabelElement.prototype.setLabelElement = function ( $label ) {
-       if ( this.$label ) {
-               this.$label.removeClass( 'oo-ui-labelElement-label' ).empty();
-       }
-
-       this.$label = $label.addClass( 'oo-ui-labelElement-label' );
-       this.setLabelContent( this.label );
-};
-
-/**
- * Set the label.
- *
- * An empty string will result in the label being hidden. A string containing only whitespace will
- * be converted to a single `&nbsp;`.
- *
- * @param {jQuery|string|OO.ui.HtmlSnippet|Function|null} label Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- * @chainable
- */
-OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
-       label = typeof label === 'function' ? OO.ui.resolveMsg( label ) : label;
-       label = ( ( typeof label === 'string' || label instanceof jQuery ) && label.length ) || ( label instanceof OO.ui.HtmlSnippet && label.toString().length ) ? label : null;
-
-       if ( this.label !== label ) {
-               if ( this.$label ) {
-                       this.setLabelContent( label );
-               }
-               this.label = label;
-               this.emit( 'labelChange' );
-       }
-
-       this.$element.toggleClass( 'oo-ui-labelElement', !!this.label );
-
-       return this;
-};
-
-/**
- * Set the label as plain text with a highlighted query
- *
- * @param {string} text Text label to set
- * @param {string} query Substring of text to highlight
- * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
- * @chainable
- */
-OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query, compare ) {
-       return this.setLabel( this.constructor.static.highlightQuery( text, query, compare ) );
-};
-
-/**
- * Get the label.
- *
- * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- */
-OO.ui.mixin.LabelElement.prototype.getLabel = function () {
-       return this.label;
-};
-
-/**
- * Set the content of the label.
- *
- * Do not call this method until after the label element has been set by #setLabelElement.
- *
- * @private
- * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
- *  text; or null for no label
- */
-OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
-       if ( typeof label === 'string' ) {
-               if ( label.match( /^\s*$/ ) ) {
-                       // Convert whitespace only string to a single non-breaking space
-                       this.$label.html( '&nbsp;' );
-               } else {
-                       this.$label.text( label );
-               }
-       } else if ( label instanceof OO.ui.HtmlSnippet ) {
-               this.$label.html( label.toString() );
-       } else if ( label instanceof jQuery ) {
-               this.$label.empty().append( label );
-       } else {
-               this.$label.empty();
-       }
-};
-
 /**
  * The FlaggedElement class is an attribute mixin, meaning that it is used to add
  * additional functionality to an element created by another class. The class provides
@@ -3252,6 +3320,7 @@ OO.ui.mixin.FlaggedElement.prototype.getFlags = function () {
  * Clear all flags.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  * @fires flag
  */
 OO.ui.mixin.FlaggedElement.prototype.clearFlags = function () {
@@ -3284,6 +3353,7 @@ OO.ui.mixin.FlaggedElement.prototype.clearFlags = function () {
  *  or an object keyed by flag name with a boolean value that indicates whether the flag should
  *  be added (`true`) or removed (`false`).
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  * @fires flag
  */
 OO.ui.mixin.FlaggedElement.prototype.setFlags = function ( flags ) {
@@ -3403,7 +3473,7 @@ OO.ui.mixin.TitledElement.static.title = null;
 /**
  * Set the titled element.
  *
- * This method is used to retarget a titledElement mixin so that its functionality applies to the specified element.
+ * This method is used to retarget a TitledElement mixin so that its functionality applies to the specified element.
  * If an element is already set, the mixin’s effect on that element is removed before the new element is set up.
  *
  * @param {jQuery} $titled Element that should use the 'titled' functionality
@@ -3424,6 +3494,7 @@ OO.ui.mixin.TitledElement.prototype.setTitledElement = function ( $titled ) {
  *
  * @param {string|Function|null} title Title text, a function that returns text, or `null` for no title
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TitledElement.prototype.setTitle = function ( title ) {
        title = typeof title === 'function' ? OO.ui.resolveMsg( title ) : title;
@@ -3442,6 +3513,7 @@ OO.ui.mixin.TitledElement.prototype.setTitle = function ( title ) {
  *
  * @protected
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.TitledElement.prototype.updateTitle = function () {
        var title = this.getTitle();
@@ -3536,7 +3608,7 @@ OO.ui.mixin.AccessKeyedElement.static.accessKey = null;
  * This method is used to retarget a AccessKeyedElement mixin so that its functionality applies to the specified element.
  * If an element is already set, the mixin's effect on that element is removed before the new element is set up.
  *
- * @param {jQuery} $accessKeyed Element that should use the 'accesskeyes' functionality
+ * @param {jQuery} $accessKeyed Element that should use the 'accesskeyed' functionality
  */
 OO.ui.mixin.AccessKeyedElement.prototype.setAccessKeyedElement = function ( $accessKeyed ) {
        if ( this.$accessKeyed ) {
@@ -3554,6 +3626,7 @@ OO.ui.mixin.AccessKeyedElement.prototype.setAccessKeyedElement = function ( $acc
  *
  * @param {string|Function|null} accessKey Key, a function that returns a key, or `null` for no accesskey
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.AccessKeyedElement.prototype.setAccessKey = function ( accessKey ) {
        accessKey = typeof accessKey === 'string' ? OO.ui.resolveMsg( accessKey ) : null;
@@ -3744,6 +3817,8 @@ OO.ui.ButtonWidget.prototype.getNoFollow = function () {
  * Set hyperlink location.
  *
  * @param {string|null} href Hyperlink location, null to remove
+ * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonWidget.prototype.setHref = function ( href ) {
        href = typeof href === 'string' ? href : null;
@@ -3765,6 +3840,7 @@ OO.ui.ButtonWidget.prototype.setHref = function ( href ) {
  *
  * @private
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonWidget.prototype.updateHref = function () {
        if ( this.href !== null && !this.isDisabled() ) {
@@ -3790,6 +3866,7 @@ OO.ui.ButtonWidget.prototype.onDisable = function () {
  * Set hyperlink target.
  *
  * @param {string|null} target Hyperlink target, null to remove
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonWidget.prototype.setTarget = function ( target ) {
        target = typeof target === 'string' ? target : null;
@@ -3810,6 +3887,7 @@ OO.ui.ButtonWidget.prototype.setTarget = function ( target ) {
  * Set search engine traversal hint.
  *
  * @param {boolean} noFollow True if search engines should avoid traversing this hyperlink
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) {
        noFollow = typeof noFollow === 'boolean' ? noFollow : true;
@@ -3904,6 +3982,7 @@ OO.ui.ButtonGroupWidget.static.tagName = 'span';
  * Focus the widget
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonGroupWidget.prototype.focus = function () {
        if ( !this.isDisabled() ) {
@@ -3944,6 +4023,7 @@ OO.ui.ButtonGroupWidget.prototype.simulateLabelClick = function () {
  * @extends OO.ui.Widget
  * @mixins OO.ui.mixin.IconElement
  * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.LabelElement
  * @mixins OO.ui.mixin.FlaggedElement
  *
  * @constructor
@@ -3959,10 +4039,14 @@ OO.ui.IconWidget = function OoUiIconWidget( config ) {
        // Mixin constructors
        OO.ui.mixin.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
        OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: this.$element, invisibleLabel: true } ) );
        OO.ui.mixin.FlaggedElement.call( this, $.extend( {}, config, { $flagged: this.$element } ) );
 
        // Initialization
        this.$element.addClass( 'oo-ui-iconWidget' );
+       // Remove class added by LabelElement initialization. It causes unexpected CSS to apply when
+       // nested in other widgets, because this widget used to not mix in LabelElement.
+       this.$element.removeClass( 'oo-ui-labelElement-label' );
 };
 
 /* Setup */
@@ -3970,6 +4054,7 @@ OO.ui.IconWidget = function OoUiIconWidget( config ) {
 OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
 OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.IconElement );
 OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.LabelElement );
 OO.mixinClass( OO.ui.IconWidget, OO.ui.mixin.FlaggedElement );
 
 /* Static Properties */
@@ -4004,6 +4089,7 @@ OO.ui.IconWidget.static.tagName = 'span';
  * @extends OO.ui.Widget
  * @mixins OO.ui.mixin.IndicatorElement
  * @mixins OO.ui.mixin.TitledElement
+ * @mixins OO.ui.mixin.LabelElement
  *
  * @constructor
  * @param {Object} [config] Configuration options
@@ -4018,9 +4104,13 @@ OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
        // Mixin constructors
        OO.ui.mixin.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
        OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+       OO.ui.mixin.LabelElement.call( this, $.extend( {}, config, { $label: this.$element, invisibleLabel: true } ) );
 
        // Initialization
        this.$element.addClass( 'oo-ui-indicatorWidget' );
+       // Remove class added by LabelElement initialization. It causes unexpected CSS to apply when
+       // nested in other widgets, because this widget used to not mix in LabelElement.
+       this.$element.removeClass( 'oo-ui-labelElement-label' );
 };
 
 /* Setup */
@@ -4028,6 +4118,7 @@ OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
 OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
 OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.IndicatorElement );
 OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.TitledElement );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.mixin.LabelElement );
 
 /* Static Properties */
 
@@ -4223,6 +4314,7 @@ OO.ui.mixin.PendingElement.prototype.isPending = function () {
  * (i.e., the number of calls to #pushPending and #popPending is the same).
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.PendingElement.prototype.pushPending = function () {
        if ( this.pending === 0 ) {
@@ -4239,6 +4331,7 @@ OO.ui.mixin.PendingElement.prototype.pushPending = function () {
  * (i.e., the number of calls to #pushPending and #popPending is the same).
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.PendingElement.prototype.popPending = function () {
        if ( this.pending === 1 ) {
@@ -4277,7 +4370,7 @@ OO.ui.mixin.PendingElement.prototype.popPending = function () {
  *  'center': Vertically align the center with $floatableContainer's center
  * @cfg {string} [horizontalPosition='start'] Where to position $floatable horizontally:
  *  'before': Directly before $floatableContainer, aligning f's end edge with fC's start edge
- *  'after': Directly after $floatableContainer, algining f's start edge with fC's end edge
+ *  'after': Directly after $floatableContainer, aligning f's start edge with fC's end edge
  *  'start': Align the start (left in LTR, right in RTL) edge with $floatableContainer's start edge
  *  'end': Align the end (right in LTR, left in RTL) edge with $floatableContainer's end edge
  *  'center': Horizontally align the center with $floatableContainer's center
@@ -4379,6 +4472,7 @@ OO.ui.mixin.FloatableElement.prototype.setHorizontalPosition = function ( positi
  *
  * @param {boolean} [positioning] Enable positioning, omit to toggle
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positioning ) {
        var closestScrollableOfContainer;
@@ -4397,11 +4491,6 @@ OO.ui.mixin.FloatableElement.prototype.togglePositioning = function ( positionin
        if ( this.positioning !== positioning ) {
                this.positioning = positioning;
 
-               this.needsCustomPosition =
-                       this.verticalPostion !== 'below' ||
-                       this.horizontalPosition !== 'start' ||
-                       !OO.ui.contains( this.$floatableContainer[ 0 ], this.$floatable[ 0 ] );
-
                closestScrollableOfContainer = OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0 ] );
                // If the scrollable is the root, we have to listen to scroll events
                // on the window because of browser inconsistencies.
@@ -4512,6 +4601,7 @@ OO.ui.mixin.FloatableElement.prototype.isFloatableOutOfView = function () {
  * This should only be done when both of them are attached to the DOM and visible.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.FloatableElement.prototype.position = function () {
        if ( !this.positioning ) {
@@ -4540,10 +4630,6 @@ OO.ui.mixin.FloatableElement.prototype.position = function () {
                this.$floatable.removeClass( 'oo-ui-element-hidden' );
        }
 
-       if ( !this.needsCustomPosition ) {
-               return this;
-       }
-
        this.$floatable.css( this.computePosition() );
 
        // We updated the position, so re-evaluate the clipping state.
@@ -4764,6 +4850,7 @@ OO.ui.mixin.ClippableElement.prototype.setClippableContainer = function ( $clipp
  *
  * @param {boolean} [clipping] Enable clipping, omit to toggle
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.ClippableElement.prototype.toggleClipping = function ( clipping ) {
        clipping = clipping === undefined ? !this.clipping : !!clipping;
@@ -4918,6 +5005,7 @@ OO.ui.mixin.ClippableElement.prototype.getVerticalAnchorEdge = function () {
  * beyond the edge, something reasonable will happen before clip() is called.
  *
  * @chainable
+ * @return {OO.ui.Element} The element, for chaining
  */
 OO.ui.mixin.ClippableElement.prototype.clip = function () {
        var extraHeight, extraWidth, viewportSpacing,
@@ -5945,6 +6033,7 @@ OO.mixinClass( OO.ui.mixin.GroupWidget, OO.ui.mixin.GroupElement );
  *
  * @param {boolean} disabled Disable widget
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.mixin.GroupWidget.prototype.setDisabled = function ( disabled ) {
        var i, len;
@@ -6000,6 +6089,7 @@ OO.ui.mixin.ItemWidget.prototype.isDisabled = function () {
  *
  * @param {OO.ui.mixin.GroupElement|null} group Group element, null if none
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.mixin.ItemWidget.prototype.setElementGroup = function ( group ) {
        // Parent method
@@ -6174,6 +6264,7 @@ OO.ui.OptionWidget.prototype.isPressed = function () {
  *
  * @param {boolean} [state=false] Select option
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
        if ( this.constructor.static.selectable ) {
@@ -6197,6 +6288,7 @@ OO.ui.OptionWidget.prototype.setSelected = function ( state ) {
  *
  * @param {boolean} [state=false] Highlight option
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
        if ( this.constructor.static.highlightable ) {
@@ -6215,6 +6307,7 @@ OO.ui.OptionWidget.prototype.setHighlighted = function ( state ) {
  *
  * @param {boolean} [state=false] Press option
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.OptionWidget.prototype.setPressed = function ( state ) {
        if ( this.constructor.static.pressable ) {
@@ -6426,6 +6519,7 @@ OO.ui.SelectWidget.prototype.onFocus = function ( event ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
        var item;
@@ -6448,6 +6542,7 @@ OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
  *
  * @private
  * @param {MouseEvent} e Mouse up event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onDocumentMouseUp = function ( e ) {
        var item;
@@ -6506,6 +6601,7 @@ OO.ui.SelectWidget.prototype.onMouseMove = function () {
  *
  * @private
  * @param {jQuery.Event} e Mouse over event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
        var item;
@@ -6524,6 +6620,7 @@ OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse over event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseLeave = function () {
        if ( !this.isDisabled() ) {
@@ -6663,6 +6760,7 @@ OO.ui.SelectWidget.prototype.clearKeyPressBuffer = function () {
  *
  * @protected
  * @param {KeyboardEvent} e Key press event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onDocumentKeyPress = function ( e ) {
        var c, filter, item;
@@ -6779,7 +6877,7 @@ OO.ui.SelectWidget.prototype.unbindDocumentKeyPressListener = function () {
 
 // Deprecated alias since 0.28.3
 OO.ui.SelectWidget.prototype.unbindKeyPressListener = function () {
-       OO.ui.warnDeprecation( 'unbindDocumentKeyPressListener is deprecated, use unbindDocumentKeyPressListener instead' );
+       OO.ui.warnDeprecation( 'unbindKeyPressListener is deprecated, use unbindDocumentKeyPressListener instead' );
        this.unbindDocumentKeyPressListener.apply( this, arguments );
 };
 
@@ -6870,6 +6968,7 @@ OO.ui.SelectWidget.prototype.togglePressed = function ( pressed ) {
  * @param {OO.ui.OptionWidget} [item] Item to highlight, omit for no highlight
  * @fires highlight
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.highlightItem = function ( item ) {
        var i, len, highlighted,
@@ -6941,6 +7040,7 @@ OO.ui.SelectWidget.prototype.getItemFromLabel = function ( label, prefix ) {
  * @param {boolean} [prefix=false] Allow a prefix match, if only a single item matches
  * @fires select
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.selectItemByLabel = function ( label, prefix ) {
        var itemFromLabel = this.getItemFromLabel( label, !!prefix );
@@ -6957,6 +7057,7 @@ OO.ui.SelectWidget.prototype.selectItemByLabel = function ( label, prefix ) {
  * @param {Object|string} [data] Value of the item to select, omit to deselect all
  * @fires select
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
        var itemFromData = this.findItemFromData( data );
@@ -6973,7 +7074,8 @@ OO.ui.SelectWidget.prototype.selectItemByData = function ( data ) {
  * @param {OO.ui.OptionWidget} [item] Item to select, omit to deselect all
  * @fires select
  * @chainable
- */
+ * @return {OO.ui.Widget} The widget, for chaining
+*/
 OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
        var i, len, selected,
                changed = false;
@@ -7009,6 +7111,7 @@ OO.ui.SelectWidget.prototype.selectItem = function ( item ) {
  * @param {OO.ui.OptionWidget} [item] Item to press, omit to depress all
  * @fires press
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
        var i, len, pressed,
@@ -7041,6 +7144,7 @@ OO.ui.SelectWidget.prototype.pressItem = function ( item ) {
  * @param {OO.ui.OptionWidget} item Item to choose
  * @fires choose
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
        if ( item ) {
@@ -7108,6 +7212,7 @@ OO.ui.SelectWidget.prototype.findFirstSelectableItem = function () {
  * @param {number} [index] Index to insert items after
  * @fires add
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
        // Mixin method
@@ -7127,6 +7232,7 @@ OO.ui.SelectWidget.prototype.addItems = function ( items, index ) {
  * @param {OO.ui.OptionWidget[]} items Items to remove
  * @fires remove
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
        var i, len, item;
@@ -7154,6 +7260,7 @@ OO.ui.SelectWidget.prototype.removeItems = function ( items ) {
  *
  * @fires remove
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.SelectWidget.prototype.clearItems = function () {
        var items = this.items.slice();
@@ -7638,6 +7745,7 @@ OO.ui.MenuSelectWidget.prototype.unbindDocumentKeyPressListener = function () {
  *
  * @param {OO.ui.OptionWidget} item Item to choose
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.MenuSelectWidget.prototype.chooseItem = function ( item ) {
        OO.ui.MenuSelectWidget.parent.prototype.chooseItem.call( this, item );
@@ -7871,7 +7979,7 @@ OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
                click: this.onClick.bind( this ),
                keydown: this.onKeyDown.bind( this ),
                // Hack? Handle type-to-search when menu is not expanded and not handling its own events
-               keypress: this.menu.onKeyPressHandler,
+               keypress: this.menu.onDocumentKeyPressHandler,
                blur: this.menu.clearKeyPressBuffer.bind( this.menu )
        } );
        this.menu.connect( this, {
@@ -7957,6 +8065,7 @@ OO.ui.DropdownWidget.prototype.onMenuToggle = function ( isVisible ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse click event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
        if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
@@ -7970,6 +8079,7 @@ OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Key down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.DropdownWidget.prototype.onKeyDown = function ( e ) {
        if (
@@ -8223,6 +8333,7 @@ OO.ui.MultioptionWidget.prototype.isSelected = function () {
  *
  * @param {boolean} [state=false] Select option
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.MultioptionWidget.prototype.setSelected = function ( state ) {
        state = !!state;
@@ -8323,6 +8434,7 @@ OO.ui.MultiselectWidget.prototype.findSelectedItemsData = function () {
  *
  * @param {OO.ui.MultioptionWidget[]} items Items to select
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.MultiselectWidget.prototype.selectItems = function ( items ) {
        this.items.forEach( function ( item ) {
@@ -8337,6 +8449,7 @@ OO.ui.MultiselectWidget.prototype.selectItems = function ( items ) {
  *
  * @param {Object[]|string[]} datas Values of items to select
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.MultiselectWidget.prototype.selectItemsByData = function ( datas ) {
        var items,
@@ -8594,6 +8707,7 @@ OO.ui.CheckboxMultiselectWidget.prototype.onClick = function ( e ) {
  * Focus the widget
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.CheckboxMultiselectWidget.prototype.focus = function () {
        var item;
@@ -8873,6 +8987,7 @@ OO.ui.InputWidget.prototype.getValue = function () {
  *
  * @param {string} dir Text directionality: 'ltr', 'rtl' or 'auto'
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.InputWidget.prototype.setDir = function ( dir ) {
        this.$input.prop( 'dir', dir );
@@ -8885,6 +9000,7 @@ OO.ui.InputWidget.prototype.setDir = function ( dir ) {
  * @param {string} value New value
  * @fires change
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.InputWidget.prototype.setValue = function ( value ) {
        value = this.cleanUpValue( value );
@@ -8942,6 +9058,7 @@ OO.ui.InputWidget.prototype.setDisabled = function ( state ) {
  *
  * @param {string} id
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.InputWidget.prototype.setInputId = function ( id ) {
        this.$input.attr( 'id', id );
@@ -9100,6 +9217,7 @@ OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
  * @param {jQuery|string|Function|null} label Label nodes, text, a function that returns nodes or
  *  text, or `null` for no label
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
        if ( typeof label === 'function' ) {
@@ -9126,6 +9244,7 @@ OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
  *
  * @param {string} value New value
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
        if ( !this.useInputTag ) {
@@ -9256,6 +9375,7 @@ OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
  *
  * @param {boolean} state `true` for selected
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.CheckboxInputWidget.prototype.setSelected = function ( state ) {
        state = !!state;
@@ -9432,6 +9552,7 @@ OO.ui.DropdownInputWidget.prototype.setDisabled = function ( state ) {
  *
  * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.DropdownInputWidget.prototype.setOptions = function ( options ) {
        var value = this.getValue();
@@ -9663,6 +9784,7 @@ OO.ui.RadioInputWidget.prototype.onEdit = function () {
  *
  * @param {boolean} state `true` for selected
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.RadioInputWidget.prototype.setSelected = function ( state ) {
        // RadioInputWidget doesn't track its state.
@@ -9833,6 +9955,7 @@ OO.ui.RadioSelectInputWidget.prototype.setDisabled = function ( state ) {
  *
  * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
        var value = this.getValue();
@@ -10049,6 +10172,7 @@ OO.ui.CheckboxMultiselectInputWidget.prototype.setDisabled = function ( state )
  *
  * @param {Object[]} options Array of menu options in the format `{ data: …, label: …, disabled: … }`
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.CheckboxMultiselectInputWidget.prototype.setOptions = function ( options ) {
        var value = this.getValue();
@@ -10283,6 +10407,7 @@ OO.ui.TextInputWidget.static.validationPatterns = {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
        if ( e.which === OO.ui.MouseButtons.LEFT ) {
@@ -10296,6 +10421,7 @@ OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
        if ( e.which === OO.ui.MouseButtons.LEFT ) {
@@ -10379,6 +10505,7 @@ OO.ui.TextInputWidget.prototype.isReadOnly = function () {
  *
  * @param {boolean} state Make input read-only
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
        this.readOnly = !!state;
@@ -10400,6 +10527,7 @@ OO.ui.TextInputWidget.prototype.isRequired = function () {
  *
  * @param {boolean} state Make input required
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.setRequired = function ( state ) {
        this.required = !!state;
@@ -10528,6 +10656,7 @@ OO.ui.TextInputWidget.prototype.getSaneType = function ( config ) {
  * @param {number} from Select from offset
  * @param {number} [to] Select to offset, defaults to from
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
        var isBackwards, start, end,
@@ -10587,6 +10716,7 @@ OO.ui.TextInputWidget.prototype.getInputLength = function () {
  * Focus the input and select the entire text.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.select = function () {
        return this.selectRange( 0, this.getInputLength() );
@@ -10596,6 +10726,7 @@ OO.ui.TextInputWidget.prototype.select = function () {
  * Focus the input and move the cursor to the start.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
        return this.selectRange( 0 );
@@ -10605,6 +10736,7 @@ OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
  * Focus the input and move the cursor to the end.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
        return this.selectRange( this.getInputLength() );
@@ -10615,6 +10747,7 @@ OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
  *
  * @param {string} content Content to be inserted
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.insertContent = function ( content ) {
        var start, end,
@@ -10635,6 +10768,7 @@ OO.ui.TextInputWidget.prototype.insertContent = function ( content ) {
  * @param {string} pre Content to be inserted before the selection
  * @param {string} post Content to be inserted after the selection
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.encapsulateContent = function ( pre, post ) {
        var start, end,
@@ -10743,6 +10877,7 @@ OO.ui.TextInputWidget.prototype.getValidity = function () {
  *
  * @param {string} labelPosition Label position, 'before' or 'after'
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
        this.labelPosition = labelPosition;
@@ -10761,6 +10896,7 @@ OO.ui.TextInputWidget.prototype.setLabelPosition = function ( labelPosition ) {
  * something causes the label to be mispositioned.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.updatePosition = function () {
        var after = this.labelPosition === 'after';
@@ -10781,6 +10917,7 @@ OO.ui.TextInputWidget.prototype.updatePosition = function () {
  *
  * @private
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.TextInputWidget.prototype.positionLabel = function () {
        var after, rtl, property, newCss;
@@ -11024,6 +11161,7 @@ OO.ui.MultilineTextInputWidget.prototype.onKeyPress = function ( e ) {
  * This only affects multiline inputs that are {@link #autosize autosized}.
  *
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  * @fires resize
  */
 OO.ui.MultilineTextInputWidget.prototype.adjustSize = function () {
@@ -11374,6 +11512,7 @@ OO.ui.ComboBoxInputWidget.prototype.setDisabled = function ( disabled ) {
  *
  * @param {Object[]} options Array of menu options in the format `{ data: …, label: … }`
  * @chainable
+ * @return {OO.ui.Widget} The widget, for chaining
  */
 OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
        this.getMenu()
@@ -11409,7 +11548,7 @@ OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) {
  * - accessed via a help icon that appears in the upper right corner of the rendered field layout, or
  * - shown as a subtle explanation below the label.
  *
- * If the help text is brief, or is essential to always espose it, set `helpInline` to `true`. If it
+ * If the help text is brief, or is essential to always expose it, set `helpInline` to `true`. If it
  * is long or not essential, leave `helpInline` to its default, `false`.
  *
  * Please see the [OOUI documentation on MediaWiki] [1] for examples and more information.
@@ -11590,6 +11729,7 @@ OO.ui.FieldLayout.prototype.makeMessage = function ( kind, text ) {
  * @private
  * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
  * @chainable
+ * @return {OO.ui.BookletLayout} The layout, for chaining
  */
 OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
        if ( value !== this.align ) {
@@ -11647,6 +11787,7 @@ OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
  * @param {Array} errors Error messages about the widget, which will be displayed below the widget.
  *  The array may contain strings or OO.ui.HtmlSnippet instances.
  * @chainable
+ * @return {OO.ui.BookletLayout} The layout, for chaining
  */
 OO.ui.FieldLayout.prototype.setErrors = function ( errors ) {
        this.errors = errors.slice();
@@ -11660,6 +11801,7 @@ OO.ui.FieldLayout.prototype.setErrors = function ( errors ) {
  * @param {Array} notices Notices about the widget, which will be displayed below the widget.
  *  The array may contain strings or OO.ui.HtmlSnippet instances.
  * @chainable
+ * @return {OO.ui.BookletLayout} The layout, for chaining
  */
 OO.ui.FieldLayout.prototype.setNotices = function ( notices ) {
        this.notices = notices.slice();
@@ -11734,7 +11876,8 @@ OO.ui.FieldLayout.prototype.createHelpElement = function ( help, $overlay ) {
                        classes: [ 'oo-ui-fieldLayout-help' ],
                        framed: false,
                        icon: 'info',
-                       label: OO.ui.msg( 'ooui-field-help' )
+                       label: OO.ui.msg( 'ooui-field-help' ),
+                       invisibleLabel: true
                } );
                if ( help instanceof OO.ui.HtmlSnippet ) {
                        helpWidget.getPopup().$body.html( help.toString() );
@@ -11905,7 +12048,8 @@ OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
                        classes: [ 'oo-ui-fieldsetLayout-help' ],
                        framed: false,
                        icon: 'info',
-                       label: OO.ui.msg( 'ooui-field-help' )
+                       label: OO.ui.msg( 'ooui-field-help' ),
+                       invisibleLabel: true
                } );
                if ( config.help instanceof OO.ui.HtmlSnippet ) {
                        this.popupButtonWidget.getPopup().$body.html( config.help.toString() );
@@ -12070,6 +12214,7 @@ OO.ui.FormLayout.static.tagName = 'form';
  * @private
  * @param {jQuery.Event} e Submit event
  * @fires submit
+ * @return {OO.ui.FormLayout} The layout, for chaining
  */
 OO.ui.FormLayout.prototype.onFormSubmit = function () {
        if ( this.emit( 'submit' ) ) {
@@ -12484,6 +12629,7 @@ OO.ui.NumberInputWidget.prototype.onButtonClick = function ( dir ) {
  *
  * @private
  * @param {jQuery.Event} event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
        var delta = 0;
@@ -12526,6 +12672,7 @@ OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
  *
  * @private
  * @param {jQuery.Event} e Key down event
+ * @return {undefined/boolean} False to prevent default if event is handled
  */
 OO.ui.NumberInputWidget.prototype.onKeyDown = function ( e ) {
        if ( !this.isDisabled() ) {