X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=resources%2Flib%2Foojs-ui%2Foojs-ui-core.js;h=1052475815de848666990bdaf85e4c5cbf829bb0;hb=babb418439588b611f0e259438372523936e257e;hp=68efe07388068de784df7bc9d893048ac58eb932;hpb=3c90317ee54ad2aa4ab0b3286e3c2eb83f364b1e;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/lib/oojs-ui/oojs-ui-core.js b/resources/lib/oojs-ui/oojs-ui-core.js index 68efe07388..6f22972dc5 100644 --- a/resources/lib/oojs-ui/oojs-ui-core.js +++ b/resources/lib/oojs-ui/oojs-ui-core.js @@ -1,12 +1,12 @@ /*! - * OOUI v0.27.0 + * OOUI v0.28.0 * 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-05-09T00:44:45Z + * Date: 2018-08-14T23:16:18Z */ ( function ( OO ) { @@ -333,11 +333,12 @@ OO.ui.now = Date.now || function () { * * @param {string|HTMLElement|jQuery} idOrNode * A DOM id (if a string) or node for the widget to infuse. + * @param {Object} [config] Configuration options * @return {OO.ui.Element} * The `OO.ui.Element` corresponding to this (infusable) document node. */ -OO.ui.infuse = function ( idOrNode ) { - return OO.ui.Element.static.infuse( idOrNode ); +OO.ui.infuse = function ( idOrNode, config ) { + return OO.ui.Element.static.infuse( idOrNode, config ); }; ( function () { @@ -685,14 +686,15 @@ OO.ui.Element.static.tagName = 'div'; * * @param {string|HTMLElement|jQuery} idOrNode * A DOM id (if a string) or node for the widget to infuse. + * @param {Object} [config] Configuration options * @return {OO.ui.Element} * The `OO.ui.Element` corresponding to this (infusable) document node. * For `Tag` objects emitted on the HTML side (used occasionally for content) * the value returned is a newly-created Element wrapping around the existing * DOM node. */ -OO.ui.Element.static.infuse = function ( idOrNode ) { - var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, false ); +OO.ui.Element.static.infuse = function ( idOrNode, config ) { + var obj = OO.ui.Element.static.unsafeInfuse( idOrNode, config, false ); // Verify that the type matches up. // FIXME: uncomment after T89721 is fixed, see T90929. /* @@ -709,12 +711,13 @@ OO.ui.Element.static.infuse = function ( idOrNode ) { * * @private * @param {string|HTMLElement|jQuery} idOrNode - * @param {jQuery.Promise|boolean} domPromise A promise that will be resolved + * @param {Object} [config] Configuration options + * @param {jQuery.Promise} [domPromise] A promise that will be resolved * when the top-level widget of this infusion is inserted into DOM, - * replacing the original node; or false for top-level invocation. + * replacing the original node; only used internally. * @return {OO.ui.Element} */ -OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) { +OO.ui.Element.static.unsafeInfuse = function ( idOrNode, config, domPromise ) { // look for a cached result of a previous infusion. var id, $elem, error, data, cls, parts, parent, obj, top, state, infusedChildren; if ( typeof idOrNode === 'string' ) { @@ -772,7 +775,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) { } if ( data._ === 'Tag' ) { // Special case: this is a raw Tag; wrap existing node, don't rebuild. - return new OO.ui.Element( { $element: $elem } ); + return new OO.ui.Element( $.extend( {}, config, { $element: $elem } ) ); } parts = data._.split( '.' ); cls = OO.getProp.apply( OO, [ window ].concat( parts ) ); @@ -796,7 +799,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) { throw new Error( 'Unknown widget type: id: ' + id + ', class: ' + data._ ); } - if ( domPromise === false ) { + if ( !domPromise ) { top = $.Deferred(); domPromise = top.promise(); } @@ -807,7 +810,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) { var infused; if ( OO.isPlainObject( value ) ) { if ( value.tag ) { - infused = OO.ui.Element.static.unsafeInfuse( value.tag, domPromise ); + infused = OO.ui.Element.static.unsafeInfuse( value.tag, config, domPromise ); infusedChildren.push( infused ); // Flatten the structure infusedChildren.push.apply( infusedChildren, infused.$element.data( 'ooui-infused-children' ) || [] ); @@ -825,7 +828,7 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) { state = cls.static.gatherPreInfuseState( $elem[ 0 ], data ); // rebuild widget // eslint-disable-next-line new-cap - obj = new cls( data ); + obj = new cls( $.extend( {}, config, 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. @@ -2669,6 +2672,7 @@ OO.ui.mixin.IconElement.prototype.setIconElement = function ( $icon ) { this.$icon = $icon .addClass( 'oo-ui-iconElement-icon' ) + .toggleClass( 'oo-ui-iconElement-noIcon', !this.icon ) .toggleClass( 'oo-ui-icon-' + this.icon, !!this.icon ); if ( this.iconTitle !== null ) { this.$icon.attr( 'title', this.iconTitle ); @@ -2703,6 +2707,9 @@ OO.ui.mixin.IconElement.prototype.setIcon = function ( icon ) { } this.$element.toggleClass( 'oo-ui-iconElement', !!this.icon ); + if ( this.$icon ) { + this.$icon.toggleClass( 'oo-ui-iconElement-noIcon', !this.icon ); + } this.updateThemeClasses(); return this; @@ -2840,6 +2847,7 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicatorElement = function ( $indicat this.$indicator = $indicator .addClass( 'oo-ui-indicatorElement-indicator' ) + .toggleClass( 'oo-ui-indicatorElement-noIndicator', !this.indicator ) .toggleClass( 'oo-ui-indicator-' + this.indicator, !!this.indicator ); if ( this.indicatorTitle !== null ) { this.$indicator.attr( 'title', this.indicatorTitle ); @@ -2870,6 +2878,9 @@ OO.ui.mixin.IndicatorElement.prototype.setIndicator = function ( indicator ) { } this.$element.toggleClass( 'oo-ui-indicatorElement', !!this.indicator ); + if ( this.$indicator ) { + this.$indicator.toggleClass( 'oo-ui-indicatorElement-noIndicator', !this.indicator ); + } this.updateThemeClasses(); return this; @@ -3602,7 +3613,7 @@ OO.ui.mixin.AccessKeyedElement.prototype.formatTitleWithAccessKey = function ( t * var button = new OO.ui.ButtonWidget( { * label: 'Button with Icon', * icon: 'trash', - * iconTitle: 'Remove' + * title: 'Remove' * } ); * $( 'body' ).append( button.$element ); * @@ -3907,7 +3918,7 @@ OO.ui.ButtonGroupWidget.prototype.simulateLabelClick = function () { * // An icon widget with a label * var myIcon = new OO.ui.IconWidget( { * icon: 'help', - * iconTitle: 'Help' + * title: 'Help' * } ); * // Create a label. * var iconLabel = new OO.ui.LabelWidget( { @@ -4118,7 +4129,7 @@ OO.ui.LabelWidget.static.tagName = 'label'; * * MessageDialog.prototype.initialize = function () { * MessageDialog.parent.prototype.initialize.apply( this, arguments ); - * this.content = new OO.ui.PanelLayout( { $: this.$, padded: true } ); + * this.content = new OO.ui.PanelLayout( { padded: true } ); * this.content.$element.append( '

Click the \'Done\' action widget to see its pending state. Note that action widgets can be marked pending in message dialogs but not process dialogs.

' ); * this.$body.append( this.content.$element ); * }; @@ -5045,7 +5056,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 [OOUI documentation on Mediawiki] [1] for more information and examples. + * Please see the [OOUI documentation on MediaWiki.org] [1] for more information and examples. * * Unlike most widgets, PopupWidget is initially hidden and must be shown by calling #toggle. * @@ -5071,8 +5082,8 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () { * * @constructor * @param {Object} [config] Configuration options - * @cfg {number} [width=320] Width of popup in pixels - * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height. + * @cfg {number|null} [width=320] Width of popup in pixels. Pass `null` to use automatic width. + * @cfg {number|null} [height=null] Height of popup in pixels. Pass `null` to use automatic height. * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup * @cfg {string} [position='below'] Where to position the popup relative to $floatableContainer * 'above': Put popup above $floatableContainer; anchor points down to the horizontal center @@ -5137,19 +5148,18 @@ OO.ui.PopupWidget = function OoUiPopupWidget( config ) { this.$container = config.$container; this.containerPadding = config.containerPadding !== undefined ? config.containerPadding : 10; this.autoClose = !!config.autoClose; - this.$autoCloseIgnore = config.$autoCloseIgnore; this.transitionTimeout = null; this.anchored = false; - this.width = config.width !== undefined ? config.width : 320; - this.height = config.height !== undefined ? config.height : null; this.onMouseDownHandler = this.onMouseDown.bind( this ); this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this ); // Initialization + this.setSize( config.width, config.height ); this.toggleAnchor( config.anchor === undefined || config.anchor ); this.setAlignment( config.align || 'center' ); this.setPosition( config.position || 'below' ); this.setAutoFlip( config.autoFlip === undefined || config.autoFlip ); + this.setAutoCloseIgnore( config.$autoCloseIgnore ); this.$body.addClass( 'oo-ui-popupWidget-body' ); this.$anchor.addClass( 'oo-ui-popupWidget-anchor' ); this.$popup @@ -5231,6 +5241,13 @@ OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) { OO.ui.PopupWidget.prototype.bindMouseDownListener = function () { // Capture clicks outside popup this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true ); + // We add 'click' event because iOS safari needs to respond to this event. + // We can't use 'touchstart' (as is usually the equivalent to 'mousedown') because + // then it will trigger when scrolling. While iOS Safari has some reported behavior + // of occasionally not emitting 'click' properly, that event seems to be the standard + // that it should be emitting, so we add it to this and will operate the event handler + // on whichever of these events was triggered first + this.getElementDocument().addEventListener( 'click', this.onMouseDownHandler, true ); }; /** @@ -5251,6 +5268,7 @@ OO.ui.PopupWidget.prototype.onCloseButtonClick = function () { */ OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () { this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true ); + this.getElementDocument().removeEventListener( 'click', this.onMouseDownHandler, true ); }; /** @@ -5447,13 +5465,13 @@ OO.ui.PopupWidget.prototype.toggle = function ( show ) { * * Changing the size may also change the popup's position depending on the alignment. * - * @param {number} width Width in pixels - * @param {number} height Height in pixels + * @param {number|null} [width=320] Width in pixels. Pass `null` to use automatic width. + * @param {number|null} [height=null] Height in pixels. Pass `null` to use automatic height. * @param {boolean} [transition=false] Use a smooth transition * @chainable */ OO.ui.PopupWidget.prototype.setSize = function ( width, height, transition ) { - this.width = width; + this.width = width !== undefined ? width : 320; this.height = height !== undefined ? height : null; if ( this.isVisible() ) { this.updateDimensions( transition ); @@ -5543,7 +5561,7 @@ OO.ui.PopupWidget.prototype.computePosition = function () { // Set height and width before we do anything else, since it might cause our measurements // to change (e.g. due to scrollbars appearing or disappearing), and it also affects centering this.$popup.css( { - width: this.width, + width: this.width !== null ? this.width : 'auto', height: this.height !== null ? this.height : 'auto' } ); @@ -5560,7 +5578,7 @@ OO.ui.PopupWidget.prototype.computePosition = function () { near = vertical ? 'top' : 'left'; far = vertical ? 'bottom' : 'right'; sizeProp = vertical ? 'Height' : 'Width'; - popupSize = vertical ? ( this.height || this.$popup.height() ) : this.width; + popupSize = vertical ? ( this.height || this.$popup.height() ) : ( this.width || this.$popup.width() ); this.setAnchorEdge( anchorEdgeMap[ popupPosition ] ); this.horizontalPosition = vertical ? popupPosition : hPosMap[ align ]; @@ -5715,6 +5733,17 @@ OO.ui.PopupWidget.prototype.setAutoFlip = function ( autoFlip ) { } }; +/** + * Set which elements will not close the popup when clicked. + * + * For auto-closing popups, clicks on these elements will not cause the popup to auto-close. + * + * @param {jQuery} $autoCloseIgnore Elements to ignore for auto-closing + */ +OO.ui.PopupWidget.prototype.setAutoCloseIgnore = function ( $autoCloseIgnore ) { + this.$autoCloseIgnore = $autoCloseIgnore; +}; + /** * Get an ID of the body element, this can be used as the * `aria-describedby` attribute for an input field. @@ -5819,8 +5848,7 @@ OO.ui.PopupButtonWidget = function OoUiPopupButtonWidget( config ) { // Initialization this.$element - .addClass( 'oo-ui-popupButtonWidget' ) - .attr( 'aria-haspopup', 'true' ); + .addClass( 'oo-ui-popupButtonWidget' ); this.popup.$element .addClass( 'oo-ui-popupButtonWidget-popup' ) .toggleClass( 'oo-ui-popupButtonWidget-framed-popup', this.isFramed() ) @@ -7281,6 +7309,11 @@ OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) { OO.ui.mixin.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$group } ) ); OO.ui.mixin.FloatableElement.call( this, config ); + // Initial vertical positions other than 'center' will result in + // the menu being flipped if there is not enough space in the container. + // Store the original position so we know what to reset to. + this.originalVerticalPosition = this.verticalPosition; + // Properties this.autoHide = config.autoHide === undefined || !!config.autoHide; this.hideOnChoose = config.hideOnChoose === undefined || !!config.hideOnChoose; @@ -7320,6 +7353,21 @@ OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.FloatableElement ); * The menu is ready: it is visible and has been positioned and clipped. */ +/* Static properties */ + +/** + * Positions to flip to if there isn't room in the container for the + * menu in a specific direction. + * + * @property {Object.} + */ +OO.ui.MenuSelectWidget.static.flippedPositions = { + below: 'above', + above: 'below', + top: 'bottom', + bottom: 'top' +}; + /* Methods */ /** @@ -7382,8 +7430,7 @@ OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) { * @protected */ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { - var i, item, visible, section, sectionEmpty, filter, exactFilter, - firstItemFound = false, + var i, item, items, visible, section, sectionEmpty, filter, exactFilter, anyVisible = false, len = this.items.length, showAll = !this.isVisible(), @@ -7392,7 +7439,6 @@ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { if ( this.$input && this.filterFromInput ) { filter = showAll ? null : this.getItemMatcher( this.$input.val() ); exactFilter = this.getItemMatcher( this.$input.val(), true ); - // Hide non-matching options, and also hide section headers if all options // in their section are hidden. for ( i = 0; i < len; i++ ) { @@ -7410,11 +7456,6 @@ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { anyVisible = anyVisible || visible; sectionEmpty = sectionEmpty && !visible; item.toggle( visible ); - if ( this.highlightOnFilter && visible && !firstItemFound ) { - // Highlight the first item in the list - this.highlightItem( item ); - firstItemFound = true; - } } } // Process the final section @@ -7427,6 +7468,20 @@ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { } this.$element.toggleClass( 'oo-ui-menuSelectWidget-invisible', !anyVisible ); + + if ( this.highlightOnFilter ) { + // Highlight the first item on the list + item = null; + items = this.getItems(); + for ( i = 0; i < items.length; i++ ) { + if ( items[ i ].isVisible() ) { + item = items[ i ]; + break; + } + } + this.highlightItem( item ); + } + } // Reevaluate clipping @@ -7551,7 +7606,7 @@ OO.ui.MenuSelectWidget.prototype.clearItems = function () { * @inheritdoc */ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) { - var change, belowHeight, aboveHeight; + var change, originalHeight, flippedHeight; visible = ( visible === undefined ? !this.visible : !!visible ) && !!this.items.length; change = visible !== this.isVisible(); @@ -7561,15 +7616,10 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) { this.warnedUnattached = true; } - if ( change ) { - if ( visible && ( this.width || this.$floatableContainer ) ) { - this.setIdealSize( this.width || this.$floatableContainer.width() ); - } - if ( visible ) { - // Reset position before showing the popup again. It's possible we no longer need to flip - // (e.g. if the user scrolled). - this.setVerticalPosition( 'below' ); - } + if ( change && visible ) { + // Reset position before showing the popup again. It's possible we no longer need to flip + // (e.g. if the user scrolled). + this.setVerticalPosition( this.originalVerticalPosition ); } // Parent method @@ -7577,22 +7627,42 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) { if ( change ) { if ( visible ) { + + if ( this.width ) { + this.setIdealSize( this.width ); + } else if ( this.$floatableContainer ) { + this.$clippable.css( 'width', 'auto' ); + this.setIdealSize( + this.$floatableContainer[ 0 ].offsetWidth > this.$clippable[ 0 ].offsetWidth ? + // Dropdown is smaller than handle so expand to width + this.$floatableContainer[ 0 ].offsetWidth : + // Dropdown is larger than handle so auto size + 'auto' + ); + this.$clippable.css( 'width', '' ); + } + this.togglePositioning( !!this.$floatableContainer ); this.toggleClipping( true ); this.bindKeyDownListener(); this.bindKeyPressListener(); - 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() || this.isFloatableOutOfView() ) && + this.originalVerticalPosition !== 'center' + ) { + // If opening the menu in one direction causes it to be clipped, flip it + originalHeight = this.$element.height(); + this.setVerticalPosition( + this.constructor.static.flippedPositions[ this.originalVerticalPosition ] + ); if ( this.isClippedVertically() || this.isFloatableOutOfView() ) { - // If opening upwards also causes it to be clipped, flip it to open in whichever direction + // If flipping also causes it to be clipped, open in whichever direction // we have more space - aboveHeight = this.$element.height(); - if ( aboveHeight < belowHeight ) { - this.setVerticalPosition( 'below' ); + flippedHeight = this.$element.height(); + if ( originalHeight > flippedHeight ) { + this.setVerticalPosition( this.originalVerticalPosition ); } } } @@ -9181,13 +9251,22 @@ OO.ui.CheckboxInputWidget.prototype.restorePreInfuseState = function ( state ) { * @param {Object} [config] Configuration options * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }` * @cfg {Object} [dropdown] Configuration options for {@link OO.ui.DropdownWidget DropdownWidget} + * @cfg {jQuery} [$overlay] Render the menu into a separate layer. This configuration is useful in cases where + * the expanded menu is larger than its containing `
`. The specified overlay layer is usually on top of the + * containing `
` and has a larger area. By default, the menu uses relative positioning. + * See . */ OO.ui.DropdownInputWidget = function OoUiDropdownInputWidget( config ) { // Configuration initialization config = config || {}; // Properties (must be done before parent constructor which calls #setDisabled) - this.dropdownWidget = new OO.ui.DropdownWidget( config.dropdown ); + this.dropdownWidget = new OO.ui.DropdownWidget( $.extend( + { + $overlay: config.$overlay + }, + 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 || [] ); @@ -10776,6 +10855,8 @@ OO.ui.MultilineTextInputWidget = function OoUiMultilineTextInputWidget( config ) if ( this.autosize ) { this.$clone = this.$input .clone() + .removeAttr( 'id' ) + .removeAttr( 'name' ) .insertAfter( this.$input ) .attr( 'aria-hidden', 'true' ) .addClass( 'oo-ui-element-hidden' ); @@ -11249,7 +11330,14 @@ OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) { * - **inline**: The label is placed after the field-widget and aligned to the left. * 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. + * Help text can either be: + * + * - 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 + * is long or not essential, leave `helpInline` to its default, `false`. + * * Please see the [OOUI documentation on MediaWiki] [1] for examples and more information. * * [1]: https://www.mediawiki.org/wiki/OOUI/Layouts/Fields_and_Fieldsets @@ -11262,15 +11350,25 @@ OO.ui.ComboBoxInputWidget.prototype.setOptions = function ( options ) { * @constructor * @param {OO.ui.Widget} fieldWidget Field widget * @param {Object} [config] Configuration options - * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' or 'inline' - * @cfg {Array} [errors] Error messages about the widget, which will be displayed below the widget. + * @cfg {string} [align='left'] Alignment of the label: 'left', 'right', 'top' + * or 'inline' + * @cfg {Array} [errors] Error messages about the widget, which will be + * displayed below the widget. * The array may contain strings or OO.ui.HtmlSnippet instances. - * @cfg {Array} [notices] Notices about the widget, which will be displayed below the widget. + * @cfg {Array} [notices] Notices about the widget, which will be displayed + * below the widget. * The array may contain strings or OO.ui.HtmlSnippet instances. - * @cfg {string|OO.ui.HtmlSnippet} [help] Help text. When help text is specified, a "help" icon will appear - * 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. + * These are more visible than `help` messages when `helpInline` is set, and so + * might be good for transient messages. + * @cfg {string|OO.ui.HtmlSnippet} [help] Help text. When help text is specified + * and `helpInline` is `false`, a "help" icon will appear in the upper-right + * corner of the rendered field; clicking it will display the text in a popup. + * If `helpInline` is `true`, then a subtle description will be shown after the + * label. + * @cfg {boolean} [helpInline=false] Whether or not the help should be inline, + * or shown when the "help" icon is clicked. + * @cfg {jQuery} [$overlay] Passed to OO.ui.PopupButtonWidget for help popup, if + * `help` is given. * See . * * @throws {Error} An error is thrown if no widget is specified @@ -11288,7 +11386,7 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) { } // Configuration initialization - config = $.extend( { align: 'left' }, config ); + config = $.extend( { align: 'left', helpInline: false }, config ); // Parent constructor OO.ui.FieldLayout.parent.call( this, config ); @@ -11308,49 +11406,29 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) { this.$header = $( '' ); this.$body = $( '
' ); this.align = null; - if ( config.help ) { - this.popupButtonWidget = new OO.ui.PopupButtonWidget( { - $overlay: config.$overlay, - popup: { - padded: true - }, - classes: [ 'oo-ui-fieldLayout-help' ], - framed: false, - icon: 'info', - label: OO.ui.msg( 'ooui-field-help' ) - } ); - if ( config.help instanceof OO.ui.HtmlSnippet ) { - this.popupButtonWidget.getPopup().$body.html( config.help.toString() ); - } else { - this.popupButtonWidget.getPopup().$body.text( config.help ); - } - this.$help = this.popupButtonWidget.$element; - } else { - this.$help = $( [] ); - } + this.helpInline = config.helpInline; // Events this.fieldWidget.connect( this, { disable: 'onFieldDisable' } ); // Initialization - if ( config.help ) { - // Set the 'aria-describedby' attribute on the fieldWidget - // Preference given to an input or a button - ( - this.fieldWidget.$input || - this.fieldWidget.$button || - this.fieldWidget.$element - ).attr( - 'aria-describedby', - this.popupButtonWidget.getPopup().getBodyId() - ); - } + this.$help = config.help ? + this.createHelpElement( config.help, config.$overlay ) : + $( [] ); if ( this.fieldWidget.getInputId() ) { this.$label.attr( 'for', this.fieldWidget.getInputId() ); + if ( this.helpInline ) { + this.$help.attr( 'for', this.fieldWidget.getInputId() ); + } } else { this.$label.on( 'click', function () { this.fieldWidget.simulateLabelClick(); }.bind( this ) ); + if ( this.helpInline ) { + this.$help.on( 'click', function () { + this.fieldWidget.simulateLabelClick(); + }.bind( this ) ); + } } this.$element .addClass( 'oo-ui-fieldLayout' ) @@ -11450,15 +11528,29 @@ OO.ui.FieldLayout.prototype.setAlignment = function ( value ) { value = 'top'; } // Reorder elements - if ( value === 'top' ) { - this.$header.append( this.$help, this.$label ); - this.$body.append( this.$header, this.$field ); - } else if ( value === 'inline' ) { - this.$header.append( this.$help, this.$label ); - this.$body.append( this.$field, this.$header ); + + if ( this.helpInline ) { + if ( value === 'top' ) { + this.$header.append( this.$label ); + this.$body.append( this.$header, this.$field, this.$help ); + } else if ( value === 'inline' ) { + this.$header.append( this.$label, this.$help ); + this.$body.append( this.$field, this.$header ); + } else { + this.$header.append( this.$label, this.$help ); + this.$body.append( this.$header, this.$field ); + } } else { - this.$header.append( this.$label ); - this.$body.append( this.$header, this.$help, this.$field ); + if ( value === 'top' ) { + this.$header.append( this.$help, this.$label ); + this.$body.append( this.$header, this.$field ); + } else if ( value === 'inline' ) { + this.$header.append( this.$help, this.$label ); + this.$body.append( this.$field, this.$header ); + } else { + this.$header.append( this.$label ); + this.$body.append( this.$header, this.$help, this.$field ); + } } // Set classes. The following classes can be used here: // * oo-ui-fieldLayout-align-left @@ -11540,6 +11632,56 @@ OO.ui.FieldLayout.prototype.formatTitleWithAccessKey = function ( title ) { return title; }; +/** + * Creates and returns the help element. Also sets the `aria-describedby` + * attribute on the main element of the `fieldWidget`. + * + * @private + * @param {string|OO.ui.HtmlSnippet} [help] Help text. + * @param {jQuery} [$overlay] Passed to OO.ui.PopupButtonWidget for help popup. + * @return {jQuery} The element that should become `this.$help`. + */ +OO.ui.FieldLayout.prototype.createHelpElement = function ( help, $overlay ) { + var helpId, helpWidget; + + if ( this.helpInline ) { + helpWidget = new OO.ui.LabelWidget( { + label: help, + classes: [ 'oo-ui-inline-help' ] + } ); + + helpId = helpWidget.getElementId(); + } else { + helpWidget = new OO.ui.PopupButtonWidget( { + $overlay: $overlay, + popup: { + padded: true + }, + classes: [ 'oo-ui-fieldLayout-help' ], + framed: false, + icon: 'info', + label: OO.ui.msg( 'ooui-field-help' ) + } ); + if ( help instanceof OO.ui.HtmlSnippet ) { + helpWidget.getPopup().$body.html( help.toString() ); + } else { + helpWidget.getPopup().$body.text( help ); + } + + helpId = helpWidget.getPopup().getBodyId(); + } + + // Set the 'aria-describedby' attribute on the fieldWidget + // Preference given to an input or a button + ( + this.fieldWidget.$input || + this.fieldWidget.$button || + this.fieldWidget.$element + ).attr( 'aria-describedby', helpId ); + + return helpWidget.$element; +}; + /** * ActionFieldLayouts are used with OO.ui.FieldsetLayout. The layout consists of a field-widget, a button, * and an optional label and/or help text. The field-widget (e.g., a {@link OO.ui.TextInputWidget TextInputWidget}), @@ -12342,4 +12484,4 @@ OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) { }( OO ) ); -//# sourceMappingURL=oojs-ui-core.js.map \ No newline at end of file +//# sourceMappingURL=oojs-ui-core.js.map.json \ No newline at end of file