From e223e8a04e56233dc9aad6f0d17e05d2ae35b84e Mon Sep 17 00:00:00 2001 From: "James D. Forrester" Date: Thu, 15 Jan 2015 16:06:44 -0800 Subject: [PATCH] Update OOjs UI to v0.6.3 Release notes: https://git.wikimedia.org/blob/oojs%2Fui.git/v0.6.3/History.md Change-Id: If0fc682b3a7f186d53ca70fdb66dded66a7f4814 --- composer.json | 2 +- resources/lib/oojs-ui/i18n/roa-tara.json | 11 +- resources/lib/oojs-ui/oojs-ui-mediawiki.css | 42 +- resources/lib/oojs-ui/oojs-ui-mediawiki.js | 4 +- .../lib/oojs-ui/oojs-ui-mediawiki.svg.css | 42 +- resources/lib/oojs-ui/oojs-ui.js | 387 +++++++++++++++++- 6 files changed, 458 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index 519274de91..bfa2ca9a12 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "require": { "cssjanus/cssjanus": "1.1.1", "leafo/lessphp": "0.5.0", - "oojs/oojs-ui": "0.6.2", + "oojs/oojs-ui": "0.6.3", "php": ">=5.3.3", "psr/log": "1.0.0", "wikimedia/cdb": "1.0.1", diff --git a/resources/lib/oojs-ui/i18n/roa-tara.json b/resources/lib/oojs-ui/i18n/roa-tara.json index cd089aff9e..f6f422a20b 100644 --- a/resources/lib/oojs-ui/i18n/roa-tara.json +++ b/resources/lib/oojs-ui/i18n/roa-tara.json @@ -6,5 +6,14 @@ }, "ooui-outline-control-move-down": "Spuèste 'a vôsce sotte", "ooui-outline-control-move-up": "Spuèste 'a vôsce sus", - "ooui-toolbar-more": "De cchiù" + "ooui-outline-control-remove": "Live 'a vôsce", + "ooui-toolbar-more": "De cchiù", + "ooui-toolgroup-expand": "De cchiù", + "ooui-toolgroup-collapse": "De mene", + "ooui-dialog-message-accept": "OK", + "ooui-dialog-message-reject": "Annulle", + "ooui-dialog-process-error": "Quacche cose ha sciute stuèrte", + "ooui-dialog-process-dismiss": "Scitte", + "ooui-dialog-process-retry": "Pruève arrete", + "ooui-dialog-process-continue": "Condinue" } diff --git a/resources/lib/oojs-ui/oojs-ui-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-mediawiki.css index 006cdebfd0..764f2eb47d 100644 --- a/resources/lib/oojs-ui/oojs-ui-mediawiki.css +++ b/resources/lib/oojs-ui/oojs-ui-mediawiki.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.6.2 + * OOjs UI v0.6.3 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2015 OOjs Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2015-01-10T01:25:31Z + * Date: 2015-01-16T00:05:16Z */ .oo-ui-progressBarWidget-slide-frames from { margin-left: -40%; @@ -381,6 +381,10 @@ display: block; background: rgba(0, 0, 0, 0.4); } +.oo-ui-lookupElement > .oo-ui-menuSelectWidget { + z-index: 1; + width: 100%; +} .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable { overflow-y: hidden; } @@ -490,6 +494,22 @@ .oo-ui-fieldLayout-disabled .oo-ui-labelElement-label { color: #cccccc; } +.oo-ui-actionFieldLayout-field { + display: table; + table-layout: fixed; + width: 100%; +} +.oo-ui-actionFieldLayout-input, +.oo-ui-actionFieldLayout-button { + display: table-cell; + vertical-align: middle; +} +.oo-ui-actionFieldLayout-input { + padding-right: 1em; +} +.oo-ui-actionFieldLayout-button { + width: 1%; +} .oo-ui-fieldsetLayout { position: relative; margin: 0; @@ -780,7 +800,8 @@ .oo-ui-listToolGroup .oo-ui-toolGroup-tools { padding: 0.25em 0 0.25em 0; border: 1px solid #aaaaaa; - box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2); + border-radius: 0.2em; + box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.1); } .oo-ui-menuToolGroup { border: solid 1px #cccccc; @@ -802,7 +823,8 @@ margin-left: -1px; padding: 0.25em 0 0.25em 0; border: 1px solid #aaaaaa; - box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2); + border-radius: 0.2em; + box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.1); } .oo-ui-menuToolGroup.oo-ui-widget-enabled:hover { border-color: #aaaaaa; @@ -1574,8 +1596,8 @@ color: #888888; } .oo-ui-dropdownWidget { - position: relative; display: inline-block; + position: relative; margin: 0.25em 0; width: 100%; max-width: 50em; @@ -1599,7 +1621,7 @@ background-position: center center; background-repeat: no-repeat; } -.oo-ui-dropdownWidget .oo-ui-menuSelectWidget { +.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget { z-index: 1; width: 100%; } @@ -1745,9 +1767,9 @@ width: 100%; max-width: 50em; } -.oo-ui-comboBoxWidget > .oo-ui-selectWidget { - width: 100%; +.oo-ui-comboBoxWidget > .oo-ui-menuSelectWidget { z-index: 1; + width: 100%; } .oo-ui-comboBoxWidget .oo-ui-textInputWidget input, .oo-ui-comboBoxWidget .oo-ui-textInputWidget textarea { @@ -1869,7 +1891,7 @@ z-index: 3; } .oo-ui-dialog-content > .oo-ui-window-body { - box-shadow: 0 0 1px 0 #aaaaaa; + outline: 1px solid #aaaaaa; } .oo-ui-messageDialog-actions-horizontal { display: table; @@ -2015,7 +2037,7 @@ } .oo-ui-processDialog-content .oo-ui-window-body { top: 3.4em; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); + outline: 1px solid rgba(0, 0, 0, 0.2); } .oo-ui-processDialog-navigation { position: relative; diff --git a/resources/lib/oojs-ui/oojs-ui-mediawiki.js b/resources/lib/oojs-ui/oojs-ui-mediawiki.js index 4f6eb59335..a0a5a32fe6 100644 --- a/resources/lib/oojs-ui/oojs-ui-mediawiki.js +++ b/resources/lib/oojs-ui/oojs-ui-mediawiki.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.6.2 + * OOjs UI v0.6.3 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2015 OOjs Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2015-01-10T01:25:19Z + * Date: 2015-01-16T00:05:04Z */ /** * @class diff --git a/resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css b/resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css index 9956340761..474304b3a8 100644 --- a/resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css +++ b/resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.6.2 + * OOjs UI v0.6.3 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2015 OOjs Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2015-01-10T01:25:31Z + * Date: 2015-01-16T00:05:16Z */ .oo-ui-progressBarWidget-slide-frames from { margin-left: -40%; @@ -381,6 +381,10 @@ display: block; background: rgba(0, 0, 0, 0.4); } +.oo-ui-lookupElement > .oo-ui-menuSelectWidget { + z-index: 1; + width: 100%; +} .oo-ui-bookletLayout-stackLayout.oo-ui-stackLayout-continuous > .oo-ui-panelLayout-scrollable { overflow-y: hidden; } @@ -490,6 +494,22 @@ .oo-ui-fieldLayout-disabled .oo-ui-labelElement-label { color: #cccccc; } +.oo-ui-actionFieldLayout-field { + display: table; + table-layout: fixed; + width: 100%; +} +.oo-ui-actionFieldLayout-input, +.oo-ui-actionFieldLayout-button { + display: table-cell; + vertical-align: middle; +} +.oo-ui-actionFieldLayout-input { + padding-right: 1em; +} +.oo-ui-actionFieldLayout-button { + width: 1%; +} .oo-ui-fieldsetLayout { position: relative; margin: 0; @@ -780,7 +800,8 @@ .oo-ui-listToolGroup .oo-ui-toolGroup-tools { padding: 0.25em 0 0.25em 0; border: 1px solid #aaaaaa; - box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2); + border-radius: 0.2em; + box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.1); } .oo-ui-menuToolGroup { border: solid 1px #cccccc; @@ -802,7 +823,8 @@ margin-left: -1px; padding: 0.25em 0 0.25em 0; border: 1px solid #aaaaaa; - box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.2); + border-radius: 0.2em; + box-shadow: inset 0 -0.2em 0 0 rgba(0, 0, 0, 0.2), 0 0.1em 0 0 rgba(0, 0, 0, 0.1); } .oo-ui-menuToolGroup.oo-ui-widget-enabled:hover { border-color: #aaaaaa; @@ -1574,8 +1596,8 @@ color: #888888; } .oo-ui-dropdownWidget { - position: relative; display: inline-block; + position: relative; margin: 0.25em 0; width: 100%; max-width: 50em; @@ -1599,7 +1621,7 @@ background-position: center center; background-repeat: no-repeat; } -.oo-ui-dropdownWidget .oo-ui-menuSelectWidget { +.oo-ui-dropdownWidget > .oo-ui-menuSelectWidget { z-index: 1; width: 100%; } @@ -1745,9 +1767,9 @@ width: 100%; max-width: 50em; } -.oo-ui-comboBoxWidget > .oo-ui-selectWidget { - width: 100%; +.oo-ui-comboBoxWidget > .oo-ui-menuSelectWidget { z-index: 1; + width: 100%; } .oo-ui-comboBoxWidget .oo-ui-textInputWidget input, .oo-ui-comboBoxWidget .oo-ui-textInputWidget textarea { @@ -1869,7 +1891,7 @@ z-index: 3; } .oo-ui-dialog-content > .oo-ui-window-body { - box-shadow: 0 0 1px 0 #aaaaaa; + outline: 1px solid #aaaaaa; } .oo-ui-messageDialog-actions-horizontal { display: table; @@ -2015,7 +2037,7 @@ } .oo-ui-processDialog-content .oo-ui-window-body { top: 3.4em; - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); + outline: 1px solid rgba(0, 0, 0, 0.2); } .oo-ui-processDialog-navigation { position: relative; diff --git a/resources/lib/oojs-ui/oojs-ui.js b/resources/lib/oojs-ui/oojs-ui.js index 38aa092615..e0c98d03e7 100644 --- a/resources/lib/oojs-ui/oojs-ui.js +++ b/resources/lib/oojs-ui/oojs-ui.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.6.2 + * OOjs UI v0.6.3 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2015 OOjs Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2015-01-10T01:25:19Z + * Date: 2015-01-16T00:05:04Z */ ( function ( OO ) { @@ -1471,8 +1471,8 @@ OO.ui.Widget.prototype.updateDisabled = function () { * * @constructor * @param {Object} [config] Configuration options - * @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`, `large` or `full`; omit to - * use #static-size + * @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`, `large`, `larger` or + * `full`; omit to use #static-size */ OO.ui.Window = function OoUiWindow( config ) { // Configuration initialization @@ -1732,7 +1732,7 @@ OO.ui.Window.prototype.getManager = function () { /** * Get the window size. * - * @return {string} Symbolic size name, e.g. 'small', 'medium', 'large', 'full' + * @return {string} Symbolic size name, e.g. `small`, `medium`, `large`, `larger`, `full` */ OO.ui.Window.prototype.getSize = function () { return this.size; @@ -2690,6 +2690,9 @@ OO.ui.WindowManager.static.sizes = { large: { width: 700 }, + larger: { + width: 900 + }, full: { // These can be non-numeric because they are never used in calculations width: '100%', @@ -4971,6 +4974,332 @@ OO.ui.LabelElement.prototype.setLabelContent = function ( label ) { } }; +/** + * Mixin that adds a menu showing suggested values for a OO.ui.TextInputWidget. + * + * Subclasses that set the value of #lookupInput from #onLookupMenuItemChoose should + * be aware that this will cause new suggestions to be looked up for the new value. If this is + * not desired, disable lookups with #setLookupsDisabled, then set the value, then re-enable lookups. + * + * @class + * @abstract + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {jQuery} [$overlay] Overlay for dropdown; defaults to relative positioning + * @cfg {jQuery} [$container=this.$element] Element to render menu under + */ +OO.ui.LookupElement = function OoUiLookupElement( config ) { + // Configuration initialization + config = config || {}; + + // Properties + this.$overlay = config.$overlay || this.$element; + this.lookupMenu = new OO.ui.TextInputMenuSelectWidget( this, { + $: OO.ui.Element.static.getJQuery( this.$overlay ), + $container: config.$container + } ); + this.lookupCache = {}; + this.lookupQuery = null; + this.lookupRequest = null; + this.lookupsDisabled = false; + this.lookupInputFocused = false; + + // Events + this.$input.on( { + focus: this.onLookupInputFocus.bind( this ), + blur: this.onLookupInputBlur.bind( this ), + mousedown: this.onLookupInputMouseDown.bind( this ) + } ); + this.connect( this, { change: 'onLookupInputChange' } ); + this.lookupMenu.connect( this, { + toggle: 'onLookupMenuToggle', + choose: 'onLookupMenuItemChoose' + } ); + + // Initialization + this.$element.addClass( 'oo-ui-lookupElement' ); + this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' ); + this.$overlay.append( this.lookupMenu.$element ); +}; + +/* Methods */ + +/** + * Handle input focus event. + * + * @param {jQuery.Event} e Input focus event + */ +OO.ui.LookupElement.prototype.onLookupInputFocus = function () { + this.lookupInputFocused = true; + this.populateLookupMenu(); +}; + +/** + * Handle input blur event. + * + * @param {jQuery.Event} e Input blur event + */ +OO.ui.LookupElement.prototype.onLookupInputBlur = function () { + this.closeLookupMenu(); + this.lookupInputFocused = false; +}; + +/** + * Handle input mouse down event. + * + * @param {jQuery.Event} e Input mouse down event + */ +OO.ui.LookupElement.prototype.onLookupInputMouseDown = function () { + // Only open the menu if the input was already focused. + // This way we allow the user to open the menu again after closing it with Esc + // by clicking in the input. Opening (and populating) the menu when initially + // clicking into the input is handled by the focus handler. + if ( this.lookupInputFocused && !this.lookupMenu.isVisible() ) { + this.populateLookupMenu(); + } +}; + +/** + * Handle input change event. + * + * @param {string} value New input value + */ +OO.ui.LookupElement.prototype.onLookupInputChange = function () { + if ( this.lookupInputFocused ) { + this.populateLookupMenu(); + } +}; + +/** + * Handle the lookup menu being shown/hidden. + * + * @param {boolean} visible Whether the lookup menu is now visible. + */ +OO.ui.LookupElement.prototype.onLookupMenuToggle = function ( visible ) { + if ( !visible ) { + // When the menu is hidden, abort any active request and clear the menu. + // This has to be done here in addition to closeLookupMenu(), because + // MenuSelectWidget will close itself when the user presses Esc. + this.abortLookupRequest(); + this.lookupMenu.clearItems(); + } +}; + +/** + * Handle menu item 'choose' event, updating the text input value to the value of the clicked item. + * + * @param {OO.ui.MenuOptionWidget|null} item Selected item + */ +OO.ui.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) { + if ( item ) { + this.setValue( item.getData() ); + } +}; + +/** + * Get lookup menu. + * + * @return {OO.ui.TextInputMenuSelectWidget} + */ +OO.ui.LookupElement.prototype.getLookupMenu = function () { + return this.lookupMenu; +}; + +/** + * Disable or re-enable lookups. + * + * When lookups are disabled, calls to #populateLookupMenu will be ignored. + * + * @param {boolean} disabled Disable lookups + */ +OO.ui.LookupElement.prototype.setLookupsDisabled = function ( disabled ) { + this.lookupsDisabled = !!disabled; +}; + +/** + * Open the menu. If there are no entries in the menu, this does nothing. + * + * @chainable + */ +OO.ui.LookupElement.prototype.openLookupMenu = function () { + if ( !this.lookupMenu.isEmpty() ) { + this.lookupMenu.toggle( true ); + } + return this; +}; + +/** + * Close the menu, empty it, and abort any pending request. + * + * @chainable + */ +OO.ui.LookupElement.prototype.closeLookupMenu = function () { + this.lookupMenu.toggle( false ); + this.abortLookupRequest(); + this.lookupMenu.clearItems(); + return this; +}; + +/** + * Request menu items based on the input's current value, and when they arrive, + * populate the menu with these items and show the menu. + * + * If lookups have been disabled with #setLookupsDisabled, this function does nothing. + * + * @chainable + */ +OO.ui.LookupElement.prototype.populateLookupMenu = function () { + var widget = this, + value = this.getValue(); + + if ( this.lookupsDisabled ) { + return; + } + + // If the input is empty, clear the menu + if ( value === '' ) { + this.closeLookupMenu(); + // Skip population if there is already a request pending for the current value + } else if ( value !== this.lookupQuery ) { + this.getLookupMenuItems() + .done( function ( items ) { + widget.lookupMenu.clearItems(); + if ( items.length ) { + widget.lookupMenu + .addItems( items ) + .toggle( true ); + widget.initializeLookupMenuSelection(); + } else { + widget.lookupMenu.toggle( false ); + } + } ) + .fail( function () { + widget.lookupMenu.clearItems(); + } ); + } + + return this; +}; + +/** + * Select and highlight the first selectable item in the menu. + * + * @chainable + */ +OO.ui.LookupElement.prototype.initializeLookupMenuSelection = function () { + if ( !this.lookupMenu.getSelectedItem() ) { + this.lookupMenu.selectItem( this.lookupMenu.getFirstSelectableItem() ); + } + this.lookupMenu.highlightItem( this.lookupMenu.getSelectedItem() ); +}; + +/** + * Get lookup menu items for the current query. + * + * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument of + * the done event. If the request was aborted to make way for a subsequent request, this promise + * will not be rejected: it will remain pending forever. + */ +OO.ui.LookupElement.prototype.getLookupMenuItems = function () { + var widget = this, + value = this.getValue(), + deferred = $.Deferred(), + ourRequest; + + this.abortLookupRequest(); + if ( Object.prototype.hasOwnProperty.call( this.lookupCache, value ) ) { + deferred.resolve( this.getLookupMenuOptionsFromData( this.lookupCache[value] ) ); + } else { + this.pushPending(); + this.lookupQuery = value; + ourRequest = this.lookupRequest = this.getLookupRequest(); + ourRequest + .always( function () { + // We need to pop pending even if this is an old request, otherwise + // the widget will remain pending forever. + // TODO: this assumes that an aborted request will fail or succeed soon after + // being aborted, or at least eventually. It would be nice if we could popPending() + // at abort time, but only if we knew that we hadn't already called popPending() + // for that request. + widget.popPending(); + } ) + .done( function ( data ) { + // If this is an old request (and aborting it somehow caused it to still succeed), + // ignore its success completely + if ( ourRequest === widget.lookupRequest ) { + widget.lookupQuery = null; + widget.lookupRequest = null; + widget.lookupCache[value] = widget.getLookupCacheDataFromResponse( data ); + deferred.resolve( widget.getLookupMenuOptionsFromData( widget.lookupCache[value] ) ); + } + } ) + .fail( function () { + // If this is an old request (or a request failing because it's being aborted), + // ignore its failure completely + if ( ourRequest === widget.lookupRequest ) { + widget.lookupQuery = null; + widget.lookupRequest = null; + deferred.reject(); + } + } ); + } + return deferred.promise(); +}; + +/** + * Abort the currently pending lookup request, if any. + */ +OO.ui.LookupElement.prototype.abortLookupRequest = function () { + var oldRequest = this.lookupRequest; + if ( oldRequest ) { + // First unset this.lookupRequest to the fail handler will notice + // that the request is no longer current + this.lookupRequest = null; + this.lookupQuery = null; + oldRequest.abort(); + } +}; + +/** + * Get a new request object of the current lookup query value. + * + * @abstract + * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method + */ +OO.ui.LookupElement.prototype.getLookupRequest = function () { + // Stub, implemented in subclass + return null; +}; + +/** + * Pre-process data returned by the request from #getLookupRequest. + * + * The return value of this function will be cached, and any further queries for the given value + * will use the cache rather than doing API requests. + * + * @abstract + * @param {Mixed} data Response from server + * @return {Mixed} Cached result data + */ +OO.ui.LookupElement.prototype.getLookupCacheDataFromResponse = function () { + // Stub, implemented in subclass + return []; +}; + +/** + * Get a list of menu option widgets from the (possibly cached) data returned by + * #getLookupCacheDataFromResponse. + * + * @abstract + * @param {Mixed} data Cached result data, usually an array + * @return {OO.ui.MenuOptionWidget[]} Menu items + */ +OO.ui.LookupElement.prototype.getLookupMenuOptionsFromData = function () { + // Stub, implemented in subclass + return []; +}; + /** * Element containing an OO.ui.PopupWidget object. * @@ -5834,7 +6163,7 @@ OO.ui.Toolbar.prototype.onPointerDown = function ( e ) { /** * Sets up handles and preloads required information for the toolbar to work. - * This must be called immediately after it is attached to a visible document. + * This must be called after it is attached to a visible document and before doing anything else. */ OO.ui.Toolbar.prototype.initialize = function () { this.initialized = true; @@ -7375,6 +7704,51 @@ OO.ui.FieldLayout.prototype.setAlignment = function ( value ) { return this; }; +/** + * Layout made of a field, a button, and an optional label. + * + * @class + * @extends OO.ui.FieldLayout + * + * @constructor + * @param {OO.ui.Widget} fieldWidget Field widget + * @param {OO.ui.ButtonWidget} buttonWidget Button widget + * @param {Object} [config] Configuration options + * @cfg {string} [align='left'] Alignment mode, either 'left', 'right', 'top' or 'inline' + * @cfg {string} [help] Explanatory text shown as a '?' icon. + */ +OO.ui.ActionFieldLayout = function OoUiActionFieldLayout( fieldWidget, buttonWidget, config ) { + // Configuration initialization + config = $.extend( { align: 'left' }, config ); + + // Properties (must be set before parent constructor, which calls #getTagName) + this.fieldWidget = fieldWidget; + this.buttonWidget = buttonWidget; + + // Parent constructor + OO.ui.ActionFieldLayout.super.call( this, fieldWidget, config ); + + // Mixin constructors + OO.ui.LabelElement.call( this, config ); + + // Properties + this.$button = this.$( '
' ) + .addClass( 'oo-ui-actionFieldLayout-button' ) + .append( this.buttonWidget.$element ); + + this.$input = this.$( '
' ) + .addClass( 'oo-ui-actionFieldLayout-input' ) + .append( this.fieldWidget.$element ); + + this.$field + .addClass( 'oo-ui-actionFieldLayout' ) + .append( this.$input, this.$button ); +}; + +/* Setup */ + +OO.inheritClass( OO.ui.ActionFieldLayout, OO.ui.FieldLayout ); + /** * Layout made of a fieldset and optional legend. * @@ -8508,6 +8882,7 @@ OO.ui.ItemWidget.prototype.setElementGroup = function ( group ) { * * @class * @abstract + * @deprecated Use LookupElement instead. * * @constructor * @param {OO.ui.TextInputWidget} input Input widget -- 2.20.1