X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=resources%2Flib%2Fooui%2Foojs-ui-widgets.js;h=a51b7af29c5d7384b524a50c5eda4fdea98599fb;hb=a23e9cee4e33a678e98a2119f469cb1f538246d4;hp=be90a2084531c3010fcdb7918fa5902abc7f915d;hpb=b9333ae6557b86e4b074025c43e77e745610b345;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/lib/ooui/oojs-ui-widgets.js b/resources/lib/ooui/oojs-ui-widgets.js index be90a20845..a51b7af29c 100644 --- a/resources/lib/ooui/oojs-ui-widgets.js +++ b/resources/lib/ooui/oojs-ui-widgets.js @@ -1,12 +1,12 @@ /*! - * OOUI v0.29.5 + * OOUI v0.30.2 * https://www.mediawiki.org/wiki/OOUI * - * Copyright 2011–2018 OOUI Team and other contributors. + * Copyright 2011–2019 OOUI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2018-11-08T22:38:07Z + * Date: 2019-01-23T01:14:20Z */ ( function ( OO ) { @@ -1288,7 +1288,7 @@ OO.ui.PageLayout.prototype.setActive = function ( active ) { * ], * continuous: true * } ); - * $( 'body' ).append( myStack.$element ); + * $( document.body ).append( myStack.$element ); * * @class * @extends OO.ui.PanelLayout @@ -1301,7 +1301,9 @@ OO.ui.PageLayout.prototype.setActive = function ( active ) { */ OO.ui.StackLayout = function OoUiStackLayout( config ) { // Configuration initialization - config = $.extend( { scrollable: true }, config ); + // Make the layout scrollable in continuous mode, otherwise each + // panel is responsible for its own scrolling. + config = $.extend( { scrollable: !!( config && config.continuous ) }, config ); // Parent constructor OO.ui.StackLayout.parent.call( this, config ); @@ -1612,7 +1614,7 @@ OO.ui.StackLayout.prototype.updateHiddenState = function ( items, selectedItem ) * menuLayout.$content.append( * contentPanel.$element.append( 'Content panel', '

Note that the menu is positioned relative to the content panel: top, bottom, after, before.

') * ); - * $( 'body' ).append( menuLayout.$element ); + * $( document.body ).append( menuLayout.$element ); * * If menu size needs to be overridden, it can be accomplished using CSS similar to the snippet * below. MenuLayout's CSS will override the appropriate values with 'auto' or '0' to display the @@ -1846,7 +1848,7 @@ OO.ui.MenuLayout.prototype.resetScroll = function () { * } ); * * booklet.addPages( [ page1, page2 ] ); - * $( 'body' ).append( booklet.$element ); + * $( document.body ).append( booklet.$element ); * * @class * @extends OO.ui.MenuLayout @@ -2112,7 +2114,7 @@ OO.ui.BookletLayout.prototype.toggleOutline = function ( show ) { // outline controls are present, delay matches transition on `.oo-ui-menuLayout-menu`. setTimeout( function () { OO.ui.Element.static.reconsiderScrollbars( booklet.outlinePanel.$element[ 0 ] ); - }, 200 ); + }, OO.ui.theme.getDialogTransitionDuration() ); } } @@ -2357,6 +2359,7 @@ OO.ui.BookletLayout.prototype.setPage = function ( name ) { ) { $focused = previousPage.$element.find( ':focus' ); if ( $focused.length ) { + // eslint-disable-next-line jquery/no-event-shorthand $focused[ 0 ].blur(); } } @@ -2369,6 +2372,7 @@ OO.ui.BookletLayout.prototype.setPage = function ( name ) { // blurred when it was hidden, but browsers are not very consistent about this. $focused = previousPage.$element.find( ':focus' ); if ( $focused.length ) { + // eslint-disable-next-line jquery/no-event-shorthand $focused[ 0 ].blur(); } } @@ -2437,7 +2441,7 @@ OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () { * var index = new OO.ui.IndexLayout(); * * index.addTabPanels( [ tabPanel1, tabPanel2 ] ); - * $( 'body' ).append( index.$element ); + * $( document.body ).append( index.$element ); * * @class * @extends OO.ui.MenuLayout @@ -2824,6 +2828,7 @@ OO.ui.IndexLayout.prototype.setTabPanel = function ( name ) { ) { $focused = previousTabPanel.$element.find( ':focus' ); if ( $focused.length ) { + // eslint-disable-next-line jquery/no-event-shorthand $focused[ 0 ].blur(); } } @@ -2836,6 +2841,7 @@ OO.ui.IndexLayout.prototype.setTabPanel = function ( name ) { // blurred when it was hidden, but browsers are not very consistent about this. $focused = previousTabPanel.$element.find( ':focus' ); if ( $focused.length ) { + // eslint-disable-next-line jquery/no-event-shorthand $focused[ 0 ].blur(); } } @@ -2865,6 +2871,7 @@ OO.ui.IndexLayout.prototype.selectFirstSelectableTabPanel = function () { * @abstract * @class * @extends OO.ui.Widget + * @mixins OO.ui.mixin.TitledElement * * @constructor * @param {Object} [config] Configuration options @@ -2878,6 +2885,9 @@ OO.ui.ToggleWidget = function OoUiToggleWidget( config ) { // Parent constructor OO.ui.ToggleWidget.parent.call( this, config ); + // Mixin constructor + OO.ui.mixin.TitledElement.call( this, config ); + // Properties this.value = null; @@ -2889,6 +2899,7 @@ OO.ui.ToggleWidget = function OoUiToggleWidget( config ) { /* Setup */ OO.inheritClass( OO.ui.ToggleWidget, OO.ui.Widget ); +OO.mixinClass( OO.ui.ToggleWidget, OO.ui.mixin.TitledElement ); /* Events */ @@ -2941,14 +2952,14 @@ OO.ui.ToggleWidget.prototype.setValue = function ( value ) { * @example * // Toggle buttons in the 'off' and 'on' state. * var toggleButton1 = new OO.ui.ToggleButtonWidget( { - * label: 'Toggle Button off' - * } ); - * var toggleButton2 = new OO.ui.ToggleButtonWidget( { - * label: 'Toggle Button on', - * value: true - * } ); + * label: 'Toggle Button off' + * } ), + * toggleButton2 = new OO.ui.ToggleButtonWidget( { + * label: 'Toggle Button on', + * value: true + * } ); * // Append the buttons to the DOM. - * $( 'body' ).append( toggleButton1.$element, toggleButton2.$element ); + * $( document.body ).append( toggleButton1.$element, toggleButton2.$element ); * * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Buttons_and_Switches#Toggle_buttons * @@ -2958,7 +2969,6 @@ OO.ui.ToggleWidget.prototype.setValue = function ( value ) { * @mixins OO.ui.mixin.IconElement * @mixins OO.ui.mixin.IndicatorElement * @mixins OO.ui.mixin.LabelElement - * @mixins OO.ui.mixin.TitledElement * @mixins OO.ui.mixin.FlaggedElement * @mixins OO.ui.mixin.TabIndexedElement * @@ -2979,7 +2989,6 @@ OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) { OO.ui.mixin.IconElement.call( this, config ); OO.ui.mixin.IndicatorElement.call( this, config ); OO.ui.mixin.LabelElement.call( this, config ); - OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) ); OO.ui.mixin.FlaggedElement.call( this, config ); OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$button } ) ); @@ -2991,6 +3000,7 @@ OO.ui.ToggleButtonWidget = function OoUiToggleButtonWidget( config ) { this.$element .addClass( 'oo-ui-toggleButtonWidget' ) .append( this.$button ); + this.setTitledElement( this.$button ); }; /* Setup */ @@ -3000,7 +3010,6 @@ OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.ButtonElement ); OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IconElement ); OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.IndicatorElement ); OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.LabelElement ); -OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TitledElement ); OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.FlaggedElement ); OO.mixinClass( OO.ui.ToggleButtonWidget, OO.ui.mixin.TabIndexedElement ); @@ -3060,20 +3069,25 @@ OO.ui.ToggleButtonWidget.prototype.setButtonElement = function ( $button ) { * * @example * // Toggle switches in the 'off' and 'on' position. - * var toggleSwitch1 = new OO.ui.ToggleSwitchWidget(); - * var toggleSwitch2 = new OO.ui.ToggleSwitchWidget( { - * value: true - * } ); - * - * // Create a FieldsetLayout to layout and label switches - * var fieldset = new OO.ui.FieldsetLayout( { - * label: 'Toggle switches' - * } ); + * var toggleSwitch1 = new OO.ui.ToggleSwitchWidget(), + * toggleSwitch2 = new OO.ui.ToggleSwitchWidget( { + * value: true + * } ); + * // Create a FieldsetLayout to layout and label switches. + * fieldset = new OO.ui.FieldsetLayout( { + * label: 'Toggle switches' + * } ); * fieldset.addItems( [ - * new OO.ui.FieldLayout( toggleSwitch1, { label: 'Off', align: 'top' } ), - * new OO.ui.FieldLayout( toggleSwitch2, { label: 'On', align: 'top' } ) + * new OO.ui.FieldLayout( toggleSwitch1, { + * label: 'Off', + * align: 'top' + * } ), + * new OO.ui.FieldLayout( toggleSwitch2, { + * label: 'On', + * align: 'top' + * } ) * ] ); - * $( 'body' ).append( fieldset.$element ); + * $( document.body ).append( fieldset.$element ); * * @class * @extends OO.ui.ToggleWidget @@ -3527,7 +3541,6 @@ OO.mixinClass( OO.ui.OutlineSelectWidget, OO.ui.mixin.TabIndexedElement ); * @mixins OO.ui.mixin.ButtonElement * @mixins OO.ui.mixin.IconElement * @mixins OO.ui.mixin.IndicatorElement - * @mixins OO.ui.mixin.TitledElement * * @constructor * @param {Object} [config] Configuration options @@ -3543,12 +3556,12 @@ OO.ui.ButtonOptionWidget = function OoUiButtonOptionWidget( config ) { OO.ui.mixin.ButtonElement.call( this, config ); OO.ui.mixin.IconElement.call( this, config ); OO.ui.mixin.IndicatorElement.call( this, config ); - OO.ui.mixin.TitledElement.call( this, $.extend( {}, config, { $titled: this.$button } ) ); // Initialization this.$element.addClass( 'oo-ui-buttonOptionWidget' ); this.$button.append( this.$icon, this.$label, this.$indicator ); this.$element.append( this.$button ); + this.setTitledElement( this.$button ); }; /* Setup */ @@ -3557,7 +3570,6 @@ OO.inheritClass( OO.ui.ButtonOptionWidget, OO.ui.OptionWidget ); OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.ButtonElement ); OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.IconElement ); OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.IndicatorElement ); -OO.mixinClass( OO.ui.ButtonOptionWidget, OO.ui.mixin.TitledElement ); /* Static Properties */ @@ -3598,29 +3610,26 @@ OO.ui.ButtonOptionWidget.prototype.setSelected = function ( state ) { * the [OOUI documentation on MediaWiki] [1] for more information. * * @example - * // Example: A ButtonSelectWidget that contains three ButtonOptionWidgets + * // A ButtonSelectWidget that contains three ButtonOptionWidgets. * var option1 = new OO.ui.ButtonOptionWidget( { - * data: 1, - * label: 'Option 1', - * title: 'Button option 1' - * } ); - * - * var option2 = new OO.ui.ButtonOptionWidget( { - * data: 2, - * label: 'Option 2', - * title: 'Button option 2' - * } ); - * - * var option3 = new OO.ui.ButtonOptionWidget( { - * data: 3, - * label: 'Option 3', - * title: 'Button option 3' - * } ); - * - * var buttonSelect=new OO.ui.ButtonSelectWidget( { - * items: [ option1, option2, option3 ] - * } ); - * $( 'body' ).append( buttonSelect.$element ); + * data: 1, + * label: 'Option 1', + * title: 'Button option 1' + * } ), + * option2 = new OO.ui.ButtonOptionWidget( { + * data: 2, + * label: 'Option 2', + * title: 'Button option 2' + * } ), + * option3 = new OO.ui.ButtonOptionWidget( { + * data: 3, + * label: 'Option 3', + * title: 'Button option 3' + * } ), + * buttonSelect = new OO.ui.ButtonSelectWidget( { + * items: [ option1, option2, option3 ] + * } ); + * $( document.body ).append( buttonSelect.$element ); * * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets/Selects_and_Options * @@ -3929,7 +3938,7 @@ OO.ui.TagItemWidget.prototype.remove = function () { OO.ui.TagItemWidget.prototype.onKeyDown = function ( e ) { var movement; - if ( !this.isDisabled() && !this.isFixed() && e.keyCode === OO.ui.Keys.BACKSPACE || e.keyCode === OO.ui.Keys.DELETE ) { + if ( !this.isDisabled() && !this.isFixed() && ( e.keyCode === OO.ui.Keys.BACKSPACE || e.keyCode === OO.ui.Keys.DELETE ) ) { this.remove(); return false; } else if ( e.keyCode === OO.ui.Keys.ENTER ) { @@ -4007,13 +4016,13 @@ OO.ui.TagItemWidget.prototype.isValid = function () { * a menu and a popup respectively. * * @example - * // Example: A basic TagMultiselectWidget. + * // A TagMultiselectWidget. * var widget = new OO.ui.TagMultiselectWidget( { * inputPosition: 'outline', * allowedValues: [ 'Option 1', 'Option 2', 'Option 3' ], * selected: [ 'Option 1' ] * } ); - * $( 'body' ).append( widget.$element ); + * $( document.body ).append( widget.$element ); * * @class * @extends OO.ui.Widget @@ -4023,6 +4032,7 @@ OO.ui.TagItemWidget.prototype.isValid = function () { * @mixins OO.ui.mixin.IconElement * @mixins OO.ui.mixin.TabIndexedElement * @mixins OO.ui.mixin.FlaggedElement + * @mixins OO.ui.mixin.TitledElement * * @constructor * @param {Object} config Configuration object @@ -4031,10 +4041,10 @@ OO.ui.TagItemWidget.prototype.isValid = function () { * replace the input widget used in the TagMultiselectWidget. If not given, * TagMultiselectWidget creates its own. * @cfg {boolean} [inputPosition='inline'] Position of the input. Options are: - * - inline: The input is invisible, but exists inside the tag list, so - * the user types into the tag groups to add tags. - * - outline: The input is underneath the tag area. - * - none: No input supplied + * - inline: The input is invisible, but exists inside the tag list, so + * the user types into the tag groups to add tags. + * - outline: The input is underneath the tag area. + * - none: No input supplied * @cfg {boolean} [allowEditTags=true] Allow editing of the tags by clicking them * @cfg {boolean} [allowArbitrary=false] Allow data items to be added even if * not present in the menu. @@ -4073,6 +4083,7 @@ OO.ui.TagMultiselectWidget = function OoUiTagMultiselectWidget( config ) { OO.ui.mixin.TabIndexedElement.call( this, config ); OO.ui.mixin.FlaggedElement.call( this, config ); OO.ui.mixin.DraggableGroupElement.call( this, config ); + OO.ui.mixin.TitledElement.call( this, config ); this.toggleDraggable( config.allowReordering === undefined ? @@ -4205,6 +4216,7 @@ OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.IndicatorElement ); OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.IconElement ); OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.TabIndexedElement ); OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.FlaggedElement ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.TitledElement ); /* Static properties */ @@ -4488,6 +4500,9 @@ OO.ui.TagMultiselectWidget.prototype.setDisabled = function ( isDisabled ) { OO.ui.TagMultiselectWidget.parent.prototype.setDisabled.call( this, isDisabled ); if ( this.hasInput && this.input ) { + if ( !isDisabled ) { + this.updateInputSize(); + } this.input.setDisabled( !!isDisabled && !this.isUnderLimit() ); } @@ -4634,10 +4649,10 @@ OO.ui.TagMultiselectWidget.prototype.getValue = function () { * This object must contain at least a data key. Example: * { data: 'foo', label: 'Foo item' } * For multiple items, use an array of objects. For example: - * [ - * { data: 'foo', label: 'Foo item' }, - * { data: 'bar', label: 'Bar item' } - * ] + * [ + * { data: 'foo', label: 'Foo item' }, + * { data: 'bar', label: 'Bar item' } + * ] * Value can also be added with plaintext array, for example: * [ 'foo', 'bar', 'bla' ] or a single string, like 'foo' */ @@ -4899,9 +4914,9 @@ OO.ui.TagMultiselectWidget.prototype.isValid = function () { * to use a popup. The popup can be configured to have a default input to insert values into the widget. * * @example - * // Example: A basic PopupTagMultiselectWidget. + * // A PopupTagMultiselectWidget. * var widget = new OO.ui.PopupTagMultiselectWidget(); - * $( 'body' ).append( widget.$element ); + * $( document.body ).append( widget.$element ); * * // Example: A PopupTagMultiselectWidget with an external popup. * var popupInput = new OO.ui.TextInputWidget(), @@ -4911,7 +4926,7 @@ OO.ui.TagMultiselectWidget.prototype.isValid = function () { * $content: popupInput.$element * } * } ); - * $( 'body' ).append( widget.$element ); + * $( document.body ).append( widget.$element ); * * @class * @extends OO.ui.TagMultiselectWidget @@ -4924,6 +4939,7 @@ OO.ui.TagMultiselectWidget.prototype.isValid = function () { * @cfg {OO.ui.InputWidget} [popupInput] An input widget inside the popup that will be * focused when the popup is opened and will be used as replacement for the * general input in the widget. + * @deprecated */ OO.ui.PopupTagMultiselectWidget = function OoUiPopupTagMultiselectWidget( config ) { var defaultInput, @@ -4983,6 +4999,9 @@ OO.ui.PopupTagMultiselectWidget = function OoUiPopupTagMultiselectWidget( config this.$element .append( this.popup.$element ) .addClass( 'oo-ui-popupTagMultiselectWidget' ); + + // Deprecation warning + OO.ui.warnDeprecation( 'PopupTagMultiselectWidget: Deprecated widget. Use MenuTagMultiselectWidget instead. See T208821.' ); }; /* Initialization */ @@ -5056,7 +5075,7 @@ OO.ui.PopupTagMultiselectWidget.prototype.addTagByPopupValue = function ( data, * to use a menu of selectable options. * * @example - * // Example: A basic MenuTagMultiselectWidget. + * // A basic MenuTagMultiselectWidget. * var widget = new OO.ui.MenuTagMultiselectWidget( { * inputPosition: 'outline', * options: [ @@ -5066,7 +5085,7 @@ OO.ui.PopupTagMultiselectWidget.prototype.addTagByPopupValue = function ( data, * ], * selected: [ 'option1', 'option2' ] * } ); - * $( 'body' ).append( widget.$element ); + * $( document.body ).append( widget.$element ); * * @class * @extends OO.ui.TagMultiselectWidget @@ -5116,6 +5135,9 @@ OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) .append( this.menu.$element ); this.$element .addClass( 'oo-ui-menuTagMultiselectWidget' ); + // Remove MenuSelectWidget's generic focus owner ARIA attribute + // TODO: Should this widget have a `role` that is compatible with this attribute? + this.menu.$focusOwner.removeAttr( 'aria-expanded' ); // TagMultiselectWidget already does this, but it doesn't work right because this.menu is not yet // set up while the parent constructor runs, and #getAllowedValues rejects everything. if ( config.selected ) { @@ -5171,11 +5193,11 @@ OO.ui.MenuTagMultiselectWidget.prototype.onInputChange = function () { * @param {OO.ui.OptionWidget} menuItem Chosen menu item */ OO.ui.MenuTagMultiselectWidget.prototype.onMenuChoose = function ( menuItem ) { - // Add tag - this.addTag( menuItem.getData(), menuItem.getLabel() ); if ( this.hasInput && this.clearInputOnChoose ) { this.input.setValue( '' ); } + // Add tag + this.addTag( menuItem.getData(), menuItem.getLabel() ); }; /** @@ -5190,6 +5212,11 @@ OO.ui.MenuTagMultiselectWidget.prototype.onMenuToggle = function ( isVisible ) { } else { this.initializeMenuSelection(); } + setTimeout( function () { + // Remove MenuSelectWidget's generic focus owner ARIA attribute + // TODO: Should this widget have a `role` that is compatible with this attribute? + this.menu.$focusOwner.removeAttr( 'aria-expanded' ); + }.bind( this ) ); }; /** @@ -5239,7 +5266,11 @@ OO.ui.MenuTagMultiselectWidget.prototype.setDisabled = function ( isDisabled ) { */ OO.ui.MenuTagMultiselectWidget.prototype.initializeMenuSelection = function () { if ( !this.menu.findSelectedItem() ) { - this.menu.highlightItem( this.menu.findFirstSelectableItem() ); + this.menu.highlightItem( + this.allowArbitrary ? + null : + this.menu.findFirstSelectableItem() + ); } }; @@ -5352,14 +5383,14 @@ OO.ui.MenuTagMultiselectWidget.prototype.getAllowedValues = function () { /** * SelectFileWidgets allow for selecting files, using the HTML5 File API. These - * widgets can be configured with {@link OO.ui.mixin.IconElement icons} and {@link - * OO.ui.mixin.IndicatorElement indicators}. + * widgets can be configured with {@link OO.ui.mixin.IconElement icons}, {@link + * OO.ui.mixin.IndicatorElement indicators} and {@link OO.ui.mixin.TitledElement titles}. * Please see the [OOUI documentation on MediaWiki] [1] for more information and examples. * * @example - * // Example of a file select widget + * // A file select widget. * var selectFile = new OO.ui.SelectFileWidget(); - * $( 'body' ).append( selectFile.$element ); + * $( document.body ).append( selectFile.$element ); * * [1]: https://www.mediawiki.org/wiki/OOUI/Widgets * @@ -5369,6 +5400,7 @@ OO.ui.MenuTagMultiselectWidget.prototype.getAllowedValues = function () { * @mixins OO.ui.mixin.IndicatorElement * @mixins OO.ui.mixin.PendingElement * @mixins OO.ui.mixin.LabelElement + * @mixins OO.ui.mixin.TitledElement * * @constructor * @param {Object} [config] Configuration options @@ -5401,6 +5433,7 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) { OO.ui.mixin.IndicatorElement.call( this, config ); OO.ui.mixin.PendingElement.call( this, $.extend( {}, config, { $pending: this.$info } ) ); OO.ui.mixin.LabelElement.call( this, config ); + OO.ui.mixin.TitledElement.call( this, config ); // Properties this.$info = $( '' ); @@ -5427,7 +5460,7 @@ OO.ui.SelectFileWidget = function OoUiSelectFileWidget( config ) { this.clearButton = new OO.ui.ButtonWidget( { classes: [ 'oo-ui-selectFileWidget-clearButton' ], framed: false, - icon: 'close', + icon: 'clear', disabled: this.disabled } ); @@ -5487,6 +5520,7 @@ OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IconElement ); OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.IndicatorElement ); OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.PendingElement ); OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.LabelElement ); +OO.mixinClass( OO.ui.SelectFileWidget, OO.ui.mixin.TitledElement ); /* Static Properties */