X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/password.php?a=blobdiff_plain;f=resources%2Flib%2Foojs-ui%2Foojs-ui-widgets.js;h=3c5d16c7bfc7acc31e4256c5d7e0d1446c1ebe60;hb=e7e607e80703c23396a2176d8f481d2d50949671;hp=a2e6e3413721b2b981bfa9b82a2669af757a6a58;hpb=bd23a0b91d8000ad273a1e70352a5f378af95629;p=lhc%2Fweb%2Fwiklou.git diff --git a/resources/lib/oojs-ui/oojs-ui-widgets.js b/resources/lib/oojs-ui/oojs-ui-widgets.js index a2e6e34137..3c5d16c7bf 100644 --- a/resources/lib/oojs-ui/oojs-ui-widgets.js +++ b/resources/lib/oojs-ui/oojs-ui-widgets.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.21.1 + * OOjs UI v0.21.4 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-04-18T23:32:49Z + * Date: 2017-05-16T22:31:39Z */ ( function ( OO ) { @@ -609,7 +609,7 @@ OO.ui.mixin.RequestManager.prototype.getRequest = null; OO.ui.mixin.RequestManager.prototype.getRequestCacheDataFromResponse = null; /** - * LookupElement is a mixin that creates a {@link OO.ui.FloatingMenuSelectWidget menu} of suggested values for + * LookupElement is a mixin that creates a {@link OO.ui.MenuSelectWidget menu} of suggested values for * a {@link OO.ui.TextInputWidget text input widget}. Suggested values are based on the characters the user types * into the text input field and, in general, the menu is only displayed when the user types. If a suggested value is chosen * from the lookup menu, that value becomes the value of the input field. @@ -628,7 +628,8 @@ OO.ui.mixin.RequestManager.prototype.getRequestCacheDataFromResponse = null; * * @constructor * @param {Object} [config] Configuration options - * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning + * @cfg {jQuery} [$overlay] Overlay for the lookup menu; defaults to relative positioning. + * See . * @cfg {jQuery} [$container=this.$element] The container element. The lookup menu is rendered beneath the specified element. * @cfg {boolean} [allowSuggestionsWhenEmpty=false] Request and display a lookup menu when the text input is empty. * By default, the lookup menu is not generated and displayed until the user begins to type. @@ -644,10 +645,10 @@ OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) { // Properties this.$overlay = config.$overlay || this.$element; - this.lookupMenu = new OO.ui.FloatingMenuSelectWidget( { + this.lookupMenu = new OO.ui.MenuSelectWidget( { widget: this, input: this, - $container: config.$container || this.$element + $floatableContainer: config.$container || this.$element } ); this.allowSuggestionsWhenEmpty = config.allowSuggestionsWhenEmpty || false; @@ -669,6 +670,11 @@ OO.ui.mixin.LookupElement = function OoUiMixinLookupElement( config ) { } ); // Initialization + this.$input.attr( { + role: 'combobox', + 'aria-owns': this.lookupMenu.getElementId(), + 'aria-autocomplete': 'list' + } ); this.$element.addClass( 'oo-ui-lookupElement' ); this.lookupMenu.$element.addClass( 'oo-ui-lookupElement-menu' ); this.$overlay.append( this.lookupMenu.$element ); @@ -760,7 +766,7 @@ OO.ui.mixin.LookupElement.prototype.onLookupMenuItemChoose = function ( item ) { * Get lookup menu. * * @private - * @return {OO.ui.FloatingMenuSelectWidget} + * @return {OO.ui.MenuSelectWidget} */ OO.ui.mixin.LookupElement.prototype.getLookupMenu = function () { return this.lookupMenu; @@ -959,23 +965,24 @@ OO.ui.mixin.LookupElement.prototype.getRequestCacheDataFromResponse = function ( }; /** - * CardLayouts are used within {@link OO.ui.IndexLayout index layouts} to create cards that users can select and display - * from the index's optional {@link OO.ui.TabSelectWidget tab} navigation. Cards are usually not instantiated directly, - * rather extended to include the required content and functionality. + * TabPanelLayouts are used within {@link OO.ui.IndexLayout index layouts} to create tab panels that + * users can select and display from the index's optional {@link OO.ui.TabSelectWidget tab} + * navigation. TabPanels are usually not instantiated directly, rather extended to include the + * required content and functionality. * - * Each card must have a unique symbolic name, which is passed to the constructor. In addition, the card's tab - * item is customized (with a label) using the #setupTabItem method. See + * Each tab panel must have a unique symbolic name, which is passed to the constructor. In addition, + * the tab panel's tab item is customized (with a label) using the #setupTabItem method. See * {@link OO.ui.IndexLayout IndexLayout} for an example. * * @class * @extends OO.ui.PanelLayout * * @constructor - * @param {string} name Unique symbolic name of card + * @param {string} name Unique symbolic name of tab panel * @param {Object} [config] Configuration options - * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] Label for card's tab + * @cfg {jQuery|string|Function|OO.ui.HtmlSnippet} [label] Label for tab panel's tab */ -OO.ui.CardLayout = function OoUiCardLayout( name, config ) { +OO.ui.TabPanelLayout = function OoUiTabPanelLayout( name, config ) { // Allow passing positional parameters inside the config object if ( OO.isPlainObject( name ) && config === undefined ) { config = name; @@ -986,7 +993,7 @@ OO.ui.CardLayout = function OoUiCardLayout( name, config ) { config = $.extend( { scrollable: true }, config ); // Parent constructor - OO.ui.CardLayout.parent.call( this, config ); + OO.ui.TabPanelLayout.parent.call( this, config ); // Properties this.name = name; @@ -995,55 +1002,56 @@ OO.ui.CardLayout = function OoUiCardLayout( name, config ) { this.active = false; // Initialization - this.$element.addClass( 'oo-ui-cardLayout' ); + this.$element.addClass( 'oo-ui-tabPanelLayout' ); }; /* Setup */ -OO.inheritClass( OO.ui.CardLayout, OO.ui.PanelLayout ); +OO.inheritClass( OO.ui.TabPanelLayout, OO.ui.PanelLayout ); /* Events */ /** - * An 'active' event is emitted when the card becomes active. Cards become active when they are - * shown in a index layout that is configured to display only one card at a time. + * An 'active' event is emitted when the tab panel becomes active. Tab panels become active when they are + * shown in a index layout that is configured to display only one tab panel at a time. * * @event active - * @param {boolean} active Card is active + * @param {boolean} active Tab panel is active */ /* Methods */ /** - * Get the symbolic name of the card. + * Get the symbolic name of the tab panel. * - * @return {string} Symbolic name of card + * @return {string} Symbolic name of tab panel */ -OO.ui.CardLayout.prototype.getName = function () { +OO.ui.TabPanelLayout.prototype.getName = function () { return this.name; }; /** - * Check if card is active. + * Check if tab panel is active. * - * Cards become active when they are shown in a {@link OO.ui.IndexLayout index layout} that is configured to display - * only one card at a time. Additional CSS is applied to the card's tab item to reflect the active state. + * Tab panels become active when they are shown in a {@link OO.ui.IndexLayout index layout} that is configured to + * display only one tab panel at a time. Additional CSS is applied to the tab panel's tab item to reflect the + * active state. * - * @return {boolean} Card is active + * @return {boolean} Tab panel is active */ -OO.ui.CardLayout.prototype.isActive = function () { +OO.ui.TabPanelLayout.prototype.isActive = function () { return this.active; }; /** * Get tab item. * - * The tab item allows users to access the card from the index's tab + * The tab item allows users to access the tab panel from the index's tab * navigation. The tab item itself can be customized (with a label, level, etc.) using the #setupTabItem method. * * @return {OO.ui.TabOptionWidget|null} Tab option widget */ -OO.ui.CardLayout.prototype.getTabItem = function () { +OO.ui.TabPanelLayout.prototype.getTabItem = function () { return this.tabItem; }; @@ -1057,7 +1065,7 @@ OO.ui.CardLayout.prototype.getTabItem = function () { * @param {OO.ui.TabOptionWidget|null} tabItem Tab option widget, null to clear * @chainable */ -OO.ui.CardLayout.prototype.setTabItem = function ( tabItem ) { +OO.ui.TabPanelLayout.prototype.setTabItem = function ( tabItem ) { this.tabItem = tabItem || null; if ( tabItem ) { this.setupTabItem(); @@ -1075,7 +1083,7 @@ OO.ui.CardLayout.prototype.setTabItem = function ( tabItem ) { * @param {OO.ui.TabOptionWidget} tabItem Tab option widget to set up * @chainable */ -OO.ui.CardLayout.prototype.setupTabItem = function () { +OO.ui.TabPanelLayout.prototype.setupTabItem = function () { if ( this.label ) { this.tabItem.setLabel( this.label ); } @@ -1083,25 +1091,42 @@ OO.ui.CardLayout.prototype.setupTabItem = function () { }; /** - * Set the card to its 'active' state. + * Set the tab panel to its 'active' state. * - * Cards become active when they are shown in a index layout that is configured to display only one card at a time. Additional - * CSS is applied to the tab item to reflect the card's active state. Outside of the index - * context, setting the active state on a card does nothing. + * Tab panels become active when they are shown in a index layout that is configured to display only + * one tab panel at a time. Additional CSS is applied to the tab item to reflect the tab panel's + * active state. Outside of the index context, setting the active state on a tab panel does nothing. * - * @param {boolean} active Card is active + * @param {boolean} active Tab panel is active * @fires active */ -OO.ui.CardLayout.prototype.setActive = function ( active ) { +OO.ui.TabPanelLayout.prototype.setActive = function ( active ) { active = !!active; if ( active !== this.active ) { this.active = active; - this.$element.toggleClass( 'oo-ui-cardLayout-active', this.active ); + this.$element.toggleClass( 'oo-ui-tabPanelLayout-active', this.active ); this.emit( 'active', this.active ); } }; +/** + * The deprecated name for the TabPanelLayout, provided for backwards-compatibility. + * + * @class + * @extends OO.ui.TabPanelLayout + * + * @constructor + * @deprecated since v0.21.3 + */ +OO.ui.CardLayout = function OoUiCardLayout() { + OO.ui.warnDeprecation( 'CardLayout has been renamed to TabPanel layout. Use that instead. See T155152' ); + // Parent constructor + OO.ui.CardLayout.parent.apply( this, arguments ); +}; + +OO.inheritClass( OO.ui.CardLayout, OO.ui.TabPanelLayout ); + /** * PageLayouts are used within {@link OO.ui.BookletLayout booklet layouts} to create pages that users can select and display * from the booklet's optional {@link OO.ui.OutlineSelectWidget outline} navigation. Pages are usually not instantiated directly, @@ -2229,34 +2254,34 @@ OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () { }; /** - * IndexLayouts contain {@link OO.ui.CardLayout card layouts} as well as - * {@link OO.ui.TabSelectWidget tabs} that allow users to easily navigate through the cards and - * select which one to display. By default, only one card is displayed at a time. When a user - * navigates to a new card, the index layout automatically focuses on the first focusable element, + * IndexLayouts contain {@link OO.ui.TabPanelLayout tab panel layouts} as well as + * {@link OO.ui.TabSelectWidget tabs} that allow users to easily navigate through the tab panels and + * select which one to display. By default, only one tab panel is displayed at a time. When a user + * navigates to a new tab panel, the index layout automatically focuses on the first focusable element, * unless the default setting is changed. * * TODO: This class is similar to BookletLayout, we may want to refactor to reduce duplication * * @example - * // Example of a IndexLayout that contains two CardLayouts. + * // Example of a IndexLayout that contains two TabPanelLayouts. * - * function CardOneLayout( name, config ) { - * CardOneLayout.parent.call( this, name, config ); - * this.$element.append( '

First card

' ); + * function TabPanelOneLayout( name, config ) { + * TabPanelOneLayout.parent.call( this, name, config ); + * this.$element.append( '

First tab panel

' ); * } - * OO.inheritClass( CardOneLayout, OO.ui.CardLayout ); - * CardOneLayout.prototype.setupTabItem = function () { - * this.tabItem.setLabel( 'Card one' ); + * OO.inheritClass( TabPanelOneLayout, OO.ui.TabPanelLayout ); + * TabPanelOneLayout.prototype.setupTabItem = function () { + * this.tabItem.setLabel( 'Tab panel one' ); * }; * - * var card1 = new CardOneLayout( 'one' ), - * card2 = new OO.ui.CardLayout( 'two', { label: 'Card two' } ); + * var tabPanel1 = new TabPanelOneLayout( 'one' ), + * tabPanel2 = new OO.ui.TabPanelLayout( 'two', { label: 'Tab panel two' } ); * - * card2.$element.append( '

Second card

' ); + * tabPanel2.$element.append( '

Second tab panel

' ); * * var index = new OO.ui.IndexLayout(); * - * index.addCards ( [ card1, card2 ] ); + * index.addTabPanels ( [ tabPanel1, tabPanel2 ] ); * $( 'body' ).append( index.$element ); * * @class @@ -2264,9 +2289,9 @@ OO.ui.BookletLayout.prototype.selectFirstSelectablePage = function () { * * @constructor * @param {Object} [config] Configuration options - * @cfg {boolean} [continuous=false] Show all cards, one after another + * @cfg {boolean} [continuous=false] Show all tab panels, one after another * @cfg {boolean} [expanded=true] Expand the content panel to fill the entire parent element. - * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new card is displayed. Disabled on mobile. + * @cfg {boolean} [autoFocus=true] Focus on the first focusable element when a new tab panel is displayed. Disabled on mobile. */ OO.ui.IndexLayout = function OoUiIndexLayout( config ) { // Configuration initialization @@ -2276,8 +2301,37 @@ OO.ui.IndexLayout = function OoUiIndexLayout( config ) { OO.ui.IndexLayout.parent.call( this, config ); // Properties - this.currentCardName = null; - this.cards = {}; + this.currentTabPanelName = null; + this.tabPanels = {}; + + Object.defineProperty( this, 'currentCardName', { + // TODO: read documentation + configurable: true, + enumerable: true, + get: function () { + OO.ui.warnDeprecation( 'IndexLayout\'s currentCardName property is deprecated. Use currentTabPanelName instead. See T155152' ); + return this.currentTabPanelName; + }, + set: function ( value ) { + OO.ui.warnDeprecation( 'IndexLayout\'s currentCardName property is deprecated. Use currentTabPanelName instead. See T155152' ); + this.currentTabPanelName = value; + } + } ); + + Object.defineProperty( this, 'cards', { + // TODO: read documentation + configurable: true, + enumerable: true, + get: function () { + OO.ui.warnDeprecation( 'IndexLayout\'s cards property is deprecated. Use tabPanels instead. See T155152' ); + return this.tabPanels; + }, + set: function ( value ) { + OO.ui.warnDeprecation( 'IndexLayout\'s cards property is deprecated. Use tabPanels instead. See T155152' ); + this.tabPanels = value; + } + } ); + this.ignoreFocus = false; this.stackLayout = new OO.ui.StackLayout( { continuous: !!config.continuous, @@ -2315,25 +2369,25 @@ OO.inheritClass( OO.ui.IndexLayout, OO.ui.MenuLayout ); /* Events */ /** - * A 'set' event is emitted when a card is {@link #setCard set} to be displayed by the index layout. + * A 'set' event is emitted when a tab panel is {@link #setTabPanel set} to be displayed by the index layout. * @event set - * @param {OO.ui.CardLayout} card Current card + * @param {OO.ui.TabPanelLayout} tabPanel Current tab panel */ /** - * An 'add' event is emitted when cards are {@link #addCards added} to the index layout. + * An 'add' event is emitted when tab panels are {@link #addTabPanels added} to the index layout. * * @event add - * @param {OO.ui.CardLayout[]} card Added cards - * @param {number} index Index cards were added at + * @param {OO.ui.TabPanelLayout[]} tabPanel Added tab panels + * @param {number} index Index tab panels were added at */ /** - * A 'remove' event is emitted when cards are {@link #clearCards cleared} or - * {@link #removeCards removed} from the index. + * A 'remove' event is emitted when tab panels are {@link #clearTabPanels cleared} or + * {@link #removeTabPanels removed} from the index. * * @event remove - * @param {OO.ui.CardLayout[]} cards Removed cards + * @param {OO.ui.TabPanelLayout[]} tabPanel Removed tab panels */ /* Methods */ @@ -2342,17 +2396,17 @@ OO.inheritClass( OO.ui.IndexLayout, OO.ui.MenuLayout ); * Handle stack layout focus. * * @private - * @param {jQuery.Event} e Focusin event + * @param {jQuery.Event} e Focusing event */ OO.ui.IndexLayout.prototype.onStackLayoutFocus = function ( e ) { var name, $target; - // Find the card that an element was focused within - $target = $( e.target ).closest( '.oo-ui-cardLayout' ); - for ( name in this.cards ) { - // Check for card match, exclude current card to find only card changes - if ( this.cards[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentCardName ) { - this.setCard( name ); + // Find the tab panel that an element was focused within + $target = $( e.target ).closest( '.oo-ui-tabPanelLayout' ); + for ( name in this.tabPanels ) { + // Check for tab panel match, exclude current tab panel to find only tab panel changes + if ( this.tabPanels[ name ].$element[ 0 ] === $target[ 0 ] && name !== this.currentTabPanelName ) { + this.setTabPanel( name ); break; } } @@ -2362,12 +2416,12 @@ OO.ui.IndexLayout.prototype.onStackLayoutFocus = function ( e ) { * Handle stack layout set events. * * @private - * @param {OO.ui.PanelLayout|null} card The card panel that is now the current panel + * @param {OO.ui.PanelLayout|null} tabPanel The tab panel that is now the current panel */ -OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( card ) { +OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( tabPanel ) { var layout = this; - if ( card ) { - card.scrollElementIntoView().done( function () { + if ( tabPanel ) { + tabPanel.scrollElementIntoView().done( function () { if ( layout.autoFocus && !OO.ui.isMobile() ) { layout.focus(); } @@ -2376,33 +2430,33 @@ OO.ui.IndexLayout.prototype.onStackLayoutSet = function ( card ) { }; /** - * Focus the first input in the current card. + * Focus the first input in the current tab panel. * - * If no card is selected, the first selectable card will be selected. - * If the focus is already in an element on the current card, nothing will happen. + * If no tab panel is selected, the first selectable tab panel will be selected. + * If the focus is already in an element on the current tab panel, nothing will happen. * * @param {number} [itemIndex] A specific item to focus on */ OO.ui.IndexLayout.prototype.focus = function ( itemIndex ) { - var card, + var tabPanel, items = this.stackLayout.getItems(); if ( itemIndex !== undefined && items[ itemIndex ] ) { - card = items[ itemIndex ]; + tabPanel = items[ itemIndex ]; } else { - card = this.stackLayout.getCurrentItem(); + tabPanel = this.stackLayout.getCurrentItem(); } - if ( !card ) { - this.selectFirstSelectableCard(); - card = this.stackLayout.getCurrentItem(); + if ( !tabPanel ) { + this.selectFirstSelectableTabPanel(); + tabPanel = this.stackLayout.getCurrentItem(); } - if ( !card ) { + if ( !tabPanel ) { return; } // Only change the focus if is not already in the current page - if ( !OO.ui.contains( card.$element[ 0 ], this.getElementDocument().activeElement, true ) ) { - card.focus(); + if ( !OO.ui.contains( tabPanel.$element[ 0 ], this.getElementDocument().activeElement, true ) ) { + tabPanel.focus(); } }; @@ -2422,26 +2476,26 @@ OO.ui.IndexLayout.prototype.focusFirstFocusable = function () { */ OO.ui.IndexLayout.prototype.onTabSelectWidgetSelect = function ( item ) { if ( item ) { - this.setCard( item.getData() ); + this.setTabPanel( item.getData() ); } }; /** - * Get the card closest to the specified card. + * Get the tab panel closest to the specified tab panel. * - * @param {OO.ui.CardLayout} card Card to use as a reference point - * @return {OO.ui.CardLayout|null} Card closest to the specified card + * @param {OO.ui.TabPanelLayout} tabPanel Tab panel to use as a reference point + * @return {OO.ui.TabPanelLayout|null} Tab panel closest to the specified */ -OO.ui.IndexLayout.prototype.getClosestCard = function ( card ) { +OO.ui.IndexLayout.prototype.getClosestTabPanel = function ( tabPanel ) { var next, prev, level, - cards = this.stackLayout.getItems(), - index = cards.indexOf( card ); + tabPanels = this.stackLayout.getItems(), + index = tabPanels.indexOf( tabPanel ); if ( index !== -1 ) { - next = cards[ index + 1 ]; - prev = cards[ index - 1 ]; - // Prefer adjacent cards at the same level - level = this.tabSelectWidget.getItemFromData( card.getName() ).getLevel(); + next = tabPanels[ index + 1 ]; + prev = tabPanels[ index - 1 ]; + // Prefer adjacent tab panels at the same level + level = this.tabSelectWidget.getItemFromData( tabPanel.getName() ).getLevel(); if ( prev && level === this.tabSelectWidget.getItemFromData( prev.getName() ).getLevel() @@ -2458,6 +2512,18 @@ OO.ui.IndexLayout.prototype.getClosestCard = function ( card ) { return prev || next || null; }; +/** + * Get the tab panel closest to the specified tab panel. + * + * @param {OO.ui.TabPanelLayout} tabPanel Tab panel to use as a reference point + * @return {OO.ui.TabPanelLayout|null} Tab panel closest to the specified + * @deprecated since v0.21.3, use `getClosestTabPanel` instead + */ +OO.ui.IndexLayout.prototype.getClosestCard = function ( tabPanel ) { + OO.ui.warnDeprecation( 'IndexLayout\'s getClosestCard method is deprecated. Use getClosestTabPanel instead. See T155152' ); + return this.getClosestTabPanel( tabPanel ); +}; + /** * Get the tabs widget. * @@ -2468,202 +2534,294 @@ OO.ui.IndexLayout.prototype.getTabs = function () { }; /** - * Get a card by its symbolic name. + * Get a tab panel by its symbolic name. * - * @param {string} name Symbolic name of card - * @return {OO.ui.CardLayout|undefined} Card, if found + * @param {string} name Symbolic name of tab panel + * @return {OO.ui.TabPanelLayout|undefined} Tab panel, if found + */ +OO.ui.IndexLayout.prototype.getTabPanel = function ( name ) { + return this.tabPanels[ name ]; +}; + +/** + * Get a tab panel by its symbolic name. + * + * @param {string} name Symbolic name of tab panel + * @return {OO.ui.TabPanelLayout|undefined} Tab panel, if found + * @deprecated since v0.21.3, use `getTabPanel` instead */ OO.ui.IndexLayout.prototype.getCard = function ( name ) { - return this.cards[ name ]; + OO.ui.warnDeprecation( 'IndexLayout\'s getCard method is deprecated. Use getTabPanel instead. See T155152' ); + return this.getTabPanel( name ); }; /** - * Get the current card. + * Get the current tab panel. * - * @return {OO.ui.CardLayout|undefined} Current card, if found + * @return {OO.ui.TabPanelLayout|undefined} Current tab panel, if found + */ +OO.ui.IndexLayout.prototype.getCurrentTabPanel = function () { + var name = this.getCurrentTabPanelName(); + return name ? this.getTabPanel( name ) : undefined; +}; + +/** + * Get the current tab panel. + * + * @return {OO.ui.TabPanelLayout|undefined} Current tab panel, if found + * @deprecated since v0.21.3, use `getCurrentTabPanel` instead */ OO.ui.IndexLayout.prototype.getCurrentCard = function () { - var name = this.getCurrentCardName(); - return name ? this.getCard( name ) : undefined; + OO.ui.warnDeprecation( 'IndexLayout\'s getCurrentCard method is deprecated. Use getCurrentTabPanel instead. See T155152' ); + return this.getCurrentTabPanel(); +}; + +/** + * Get the symbolic name of the current tab panel. + * + * @return {string|null} Symbolic name of the current tab panel + */ +OO.ui.IndexLayout.prototype.getCurrentTabPanelName = function () { + return this.currentTabPanelName; }; /** - * Get the symbolic name of the current card. + * Get the symbolic name of the current tab panel. * - * @return {string|null} Symbolic name of the current card + * @return {string|null} Symbolic name of the current tab panel + * @deprecated since v0.21.3, use `getCurrentTabPanelName` instead */ OO.ui.IndexLayout.prototype.getCurrentCardName = function () { - return this.currentCardName; + OO.ui.warnDeprecation( 'IndexLayout\'s getCurrentCardName method is deprecated. Use getCurrentTabPanelName instead. See T155152' ); + return this.getCurrentTabPanelName(); }; /** - * Add cards to the index layout + * Add tab panels to the index layout * - * When cards are added with the same names as existing cards, the existing cards will be - * automatically removed before the new cards are added. + * When tab panels are added with the same names as existing tab panels, the existing tab panels + * will be automatically removed before the new tab panels are added. * - * @param {OO.ui.CardLayout[]} cards Cards to add + * @param {OO.ui.TabPanelLayout[]} tabPanels Tab panels to add * @param {number} index Index of the insertion point * @fires add * @chainable */ -OO.ui.IndexLayout.prototype.addCards = function ( cards, index ) { - var i, len, name, card, item, currentIndex, - stackLayoutCards = this.stackLayout.getItems(), +OO.ui.IndexLayout.prototype.addTabPanels = function ( tabPanels, index ) { + var i, len, name, tabPanel, item, currentIndex, + stackLayoutTabPanels = this.stackLayout.getItems(), remove = [], items = []; - // Remove cards with same names - for ( i = 0, len = cards.length; i < len; i++ ) { - card = cards[ i ]; - name = card.getName(); + // Remove tab panels with same names + for ( i = 0, len = tabPanels.length; i < len; i++ ) { + tabPanel = tabPanels[ i ]; + name = tabPanel.getName(); - if ( Object.prototype.hasOwnProperty.call( this.cards, name ) ) { + if ( Object.prototype.hasOwnProperty.call( this.tabPanels, name ) ) { // Correct the insertion index - currentIndex = stackLayoutCards.indexOf( this.cards[ name ] ); + currentIndex = stackLayoutTabPanels.indexOf( this.tabPanels[ name ] ); if ( currentIndex !== -1 && currentIndex + 1 < index ) { index--; } - remove.push( this.cards[ name ] ); + remove.push( this.tabPanels[ name ] ); } } if ( remove.length ) { - this.removeCards( remove ); + this.removeTabPanels( remove ); } - // Add new cards - for ( i = 0, len = cards.length; i < len; i++ ) { - card = cards[ i ]; - name = card.getName(); - this.cards[ card.getName() ] = card; + // Add new tab panels + for ( i = 0, len = tabPanels.length; i < len; i++ ) { + tabPanel = tabPanels[ i ]; + name = tabPanel.getName(); + this.tabPanels[ tabPanel.getName() ] = tabPanel; item = new OO.ui.TabOptionWidget( { data: name } ); - card.setTabItem( item ); + tabPanel.setTabItem( item ); items.push( item ); } if ( items.length ) { this.tabSelectWidget.addItems( items, index ); - this.selectFirstSelectableCard(); + this.selectFirstSelectableTabPanel(); } - this.stackLayout.addItems( cards, index ); - this.emit( 'add', cards, index ); + this.stackLayout.addItems( tabPanels, index ); + this.emit( 'add', tabPanels, index ); return this; }; /** - * Remove the specified cards from the index layout. + * Add tab panels to the index layout * - * To remove all cards from the index, you may wish to use the #clearCards method instead. + * When tab panels are added with the same names as existing tab panels, the existing tab panels + * will be automatically removed before the new tab panels are added. * - * @param {OO.ui.CardLayout[]} cards An array of cards to remove + * @param {OO.ui.TabPanelLayout[]} tabPanels Tab panels to add + * @param {number} index Index of the insertion point + * @fires add + * @chainable + * @deprecated since v0.21.3, use `addTabPanels` instead + */ +OO.ui.IndexLayout.prototype.addCards = function ( tabPanels, index ) { + OO.ui.warnDeprecation( 'IndexLayout\'s addCards method is deprecated. Use addTabPanels instead. See T155152' ); + return this.addTabPanels( tabPanels, index ); +}; + +/** + * Remove the specified tab panels from the index layout. + * + * To remove all tab panels from the index, you may wish to use the #clearTabPanels method instead. + * + * @param {OO.ui.TabPanelLayout[]} tabPanels An array of tab panels to remove * @fires remove * @chainable */ -OO.ui.IndexLayout.prototype.removeCards = function ( cards ) { - var i, len, name, card, +OO.ui.IndexLayout.prototype.removeTabPanels = function ( tabPanels ) { + var i, len, name, tabPanel, items = []; - for ( i = 0, len = cards.length; i < len; i++ ) { - card = cards[ i ]; - name = card.getName(); - delete this.cards[ name ]; + for ( i = 0, len = tabPanels.length; i < len; i++ ) { + tabPanel = tabPanels[ i ]; + name = tabPanel.getName(); + delete this.tabPanels[ name ]; items.push( this.tabSelectWidget.getItemFromData( name ) ); - card.setTabItem( null ); + tabPanel.setTabItem( null ); } if ( items.length ) { this.tabSelectWidget.removeItems( items ); - this.selectFirstSelectableCard(); + this.selectFirstSelectableTabPanel(); } - this.stackLayout.removeItems( cards ); - this.emit( 'remove', cards ); + this.stackLayout.removeItems( tabPanels ); + this.emit( 'remove', tabPanels ); return this; }; /** - * Clear all cards from the index layout. + * Remove the specified tab panels from the index layout. * - * To remove only a subset of cards from the index, use the #removeCards method. + * To remove all tab panels from the index, you may wish to use the #clearTabPanels method instead. * + * @param {OO.ui.TabPanelLayout[]} tabPanels An array of tab panels to remove * @fires remove * @chainable + * @deprecated since v0.21.3, use `removeTabPanels` instead */ -OO.ui.IndexLayout.prototype.clearCards = function () { +OO.ui.IndexLayout.prototype.removeCards = function ( tabPanels ) { + OO.ui.warnDeprecation( 'IndexLayout\'s removeCards method is deprecated. Use removeTabPanels instead. See T155152.' ); + return this.removeTabPanels( tabPanels ); +}; + +/** + * Clear all tab panels from the index layout. + * + * To remove only a subset of tab panels from the index, use the #removeTabPanels method. + * + * @fires remove + * @chainable + */ +OO.ui.IndexLayout.prototype.clearTabPanels = function () { var i, len, - cards = this.stackLayout.getItems(); + tabPanels = this.stackLayout.getItems(); - this.cards = {}; - this.currentCardName = null; + this.tabPanels = {}; + this.currentTabPanelName = null; this.tabSelectWidget.clearItems(); - for ( i = 0, len = cards.length; i < len; i++ ) { - cards[ i ].setTabItem( null ); + for ( i = 0, len = tabPanels.length; i < len; i++ ) { + tabPanels[ i ].setTabItem( null ); } this.stackLayout.clearItems(); - this.emit( 'remove', cards ); + this.emit( 'remove', tabPanels ); return this; }; /** - * Set the current card by symbolic name. + * Clear all tab panels from the index layout. + * + * To remove only a subset of tab panels from the index, use the #removeTabPanels method. + * + * @fires remove + * @chainable + * @deprecated since v0.21.3, use `clearTabPanels` instead + */ +OO.ui.IndexLayout.prototype.clearCards = function () { + OO.ui.warnDeprecation( 'IndexLayout\'s clearCards method is deprecated. Use clearTabPanels instead. See T155152.' ); + return this.clearTabPanels(); +}; + +/** + * Set the current tab panel by symbolic name. * * @fires set - * @param {string} name Symbolic name of card + * @param {string} name Symbolic name of tab panel */ -OO.ui.IndexLayout.prototype.setCard = function ( name ) { +OO.ui.IndexLayout.prototype.setTabPanel = function ( name ) { var selectedItem, $focused, - card = this.cards[ name ], - previousCard = this.currentCardName && this.cards[ this.currentCardName ]; + tabPanel = this.tabPanels[ name ], + previousTabPanel = this.currentTabPanelName && this.tabPanels[ this.currentTabPanelName ]; - if ( name !== this.currentCardName ) { + if ( name !== this.currentTabPanelName ) { selectedItem = this.tabSelectWidget.getSelectedItem(); if ( selectedItem && selectedItem.getData() !== name ) { this.tabSelectWidget.selectItemByData( name ); } - if ( card ) { - if ( previousCard ) { - previousCard.setActive( false ); - // Blur anything focused if the next card doesn't have anything focusable. - // This is not needed if the next card has something focusable (because once it is focused + if ( tabPanel ) { + if ( previousTabPanel ) { + previousTabPanel.setActive( false ); + // Blur anything focused if the next tab panel doesn't have anything focusable. + // This is not needed if the next tab panel has something focusable (because once it is focused // this blur happens automatically). If the layout is non-continuous, this check is - // meaningless because the next card is not visible yet and thus can't hold focus. + // meaningless because the next tab panel is not visible yet and thus can't hold focus. if ( this.autoFocus && !OO.ui.isMobile() && this.stackLayout.continuous && - OO.ui.findFocusable( card.$element ).length !== 0 + OO.ui.findFocusable( tabPanel.$element ).length !== 0 ) { - $focused = previousCard.$element.find( ':focus' ); + $focused = previousTabPanel.$element.find( ':focus' ); if ( $focused.length ) { $focused[ 0 ].blur(); } } } - this.currentCardName = name; - card.setActive( true ); - this.stackLayout.setItem( card ); - if ( !this.stackLayout.continuous && previousCard ) { - // This should not be necessary, since any inputs on the previous card should have been + this.currentTabPanelName = name; + tabPanel.setActive( true ); + this.stackLayout.setItem( tabPanel ); + if ( !this.stackLayout.continuous && previousTabPanel ) { + // This should not be necessary, since any inputs on the previous tab panel should have been // blurred when it was hidden, but browsers are not very consistent about this. - $focused = previousCard.$element.find( ':focus' ); + $focused = previousTabPanel.$element.find( ':focus' ); if ( $focused.length ) { $focused[ 0 ].blur(); } } - this.emit( 'set', card ); + this.emit( 'set', tabPanel ); } } }; /** - * Select the first selectable card. + * Set the current tab panel by symbolic name. + * + * @fires set + * @param {string} name Symbolic name of tab panel + * @deprecated since v0.21.3, use `setTabPanel` instead + */ +OO.ui.IndexLayout.prototype.setCard = function ( name ) { + OO.ui.warnDeprecation( 'IndexLayout\'s setCard method is deprecated. Use setTabPanel instead. See T155152.' ); + return this.setTabPanel( name ); +}; + +/** + * Select the first selectable tab panel. * * @chainable */ -OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () { +OO.ui.IndexLayout.prototype.selectFirstSelectableTabPanel = function () { if ( !this.tabSelectWidget.getSelectedItem() ) { this.tabSelectWidget.selectItem( this.tabSelectWidget.getFirstSelectableItem() ); } @@ -2671,6 +2829,17 @@ OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () { return this; }; +/** + * Select the first selectable tab panel. + * + * @chainable + * @deprecated since v0.21.3, use `selectFirstSelectableTabPanel` instead + */ +OO.ui.IndexLayout.prototype.selectFirstSelectableCard = function () { + OO.ui.warnDeprecation( 'IndexLayout\'s selectFirstSelectableCard method is deprecated. Use selectFirestSelectableTabPanel instead. See T155152.' ); + return this.selectFirstSelectableTabPanel(); +}; + /** * ToggleWidget implements basic behavior of widgets with an on/off state. * Please see OO.ui.ToggleButtonWidget and OO.ui.ToggleSwitchWidget for examples. @@ -2725,7 +2894,7 @@ OO.ui.ToggleWidget.prototype.getValue = function () { }; /** - * Set the state of the toggle: `true` for 'on', `false' for 'off'. + * Set the state of the toggle: `true` for 'on', `false` for 'off'. * * @param {boolean} value The state of the toggle * @fires change @@ -2738,7 +2907,6 @@ OO.ui.ToggleWidget.prototype.setValue = function ( value ) { this.emit( 'change', value ); this.$element.toggleClass( 'oo-ui-toggleWidget-on', value ); this.$element.toggleClass( 'oo-ui-toggleWidget-off', !value ); - this.$element.attr( 'aria-checked', value.toString() ); } return this; }; @@ -2959,6 +3127,15 @@ OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) { } }; +/** + * @inheritdoc + */ +OO.ui.ToggleSwitchWidget.prototype.setValue = function ( value ) { + OO.ui.ToggleSwitchWidget.parent.prototype.setValue.call( this, value ); + this.$element.attr( 'aria-checked', this.value.toString() ); + return this; +}; + /** * OutlineControlsWidget is a set of controls for an {@link OO.ui.OutlineSelectWidget outline select widget}. * Controls include moving items up and down, removing items, and adding different kinds of items. @@ -3459,7 +3636,7 @@ OO.mixinClass( OO.ui.ButtonSelectWidget, OO.ui.mixin.TabIndexedElement ); * TabOptionWidget is an item in a {@link OO.ui.TabSelectWidget TabSelectWidget}. * * Currently, this class is only used by {@link OO.ui.IndexLayout index layouts}, which contain - * {@link OO.ui.CardLayout card layouts}. See {@link OO.ui.IndexLayout IndexLayout} + * {@link OO.ui.TabPanelLayout tab panel layouts}. See {@link OO.ui.IndexLayout IndexLayout} * for an example. * * @class @@ -3628,13 +3805,6 @@ OO.ui.CapsuleItemWidget.prototype.onKeyDown = function ( e ) { } }; -/** - * Focuses the capsule - */ -OO.ui.CapsuleItemWidget.prototype.focus = function () { - this.$element.focus(); -}; - /** * CapsuleMultiselectWidgets are something like a {@link OO.ui.ComboBoxInputWidget combo box widget} * that allows for selecting multiple values. @@ -3683,7 +3853,7 @@ OO.ui.CapsuleItemWidget.prototype.focus = function () { * @mixins OO.ui.mixin.IndicatorElement * @mixins OO.ui.mixin.IconElement * @uses OO.ui.CapsuleItemWidget - * @uses OO.ui.FloatingMenuSelectWidget + * @uses OO.ui.MenuSelectWidget * * @constructor * @param {Object} [config] Configuration options @@ -3701,6 +3871,7 @@ OO.ui.CapsuleItemWidget.prototype.focus = function () { * 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.CapsuleMultiselectWidget = function OoUiCapsuleMultiselectWidget( config ) { var $tabFocus; @@ -3745,11 +3916,11 @@ OO.ui.CapsuleMultiselectWidget = function OoUiCapsuleMultiselectWidget( config ) this.allowArbitrary = config.allowArbitrary; this.allowDuplicates = config.allowDuplicates; this.$overlay = config.$overlay; - this.menu = new OO.ui.FloatingMenuSelectWidget( $.extend( + this.menu = new OO.ui.MenuSelectWidget( $.extend( { widget: this, $input: this.$input, - $container: this.$element, + $floatableContainer: this.$element, filterFromInput: true, disabled: this.isDisabled() }, @@ -3795,6 +3966,7 @@ OO.ui.CapsuleMultiselectWidget = function OoUiCapsuleMultiselectWidget( config ) this.$input.prop( 'disabled', this.isDisabled() ); this.$input.attr( { role: 'combobox', + 'aria-owns': this.menu.getElementId(), 'aria-autocomplete': 'list' } ); } @@ -3839,10 +4011,6 @@ OO.mixinClass( OO.ui.CapsuleMultiselectWidget, OO.ui.mixin.TabIndexedElement ); OO.mixinClass( OO.ui.CapsuleMultiselectWidget, OO.ui.mixin.IndicatorElement ); OO.mixinClass( OO.ui.CapsuleMultiselectWidget, OO.ui.mixin.IconElement ); -/* Static Properties */ - -OO.ui.CapsuleMultiselectWidget.static.supportsSimpleLabel = true; - /* Events */ /** @@ -3879,23 +4047,13 @@ OO.ui.CapsuleMultiselectWidget.prototype.createItemWidget = function ( data, lab }; /** - * Get the widget's input's id, or generate one, if it has an input. - * - * @return {string} + * @inheritdoc */ OO.ui.CapsuleMultiselectWidget.prototype.getInputId = function () { - var id; if ( !this.$input ) { - return false; - } - - id = this.$input.attr( 'id' ); - if ( id === undefined ) { - id = OO.ui.generateElementId(); - this.$input.attr( 'id', id ); + return null; } - - return id; + return OO.ui.mixin.TabIndexedElement.prototype.getInputId.call( this ); }; /** @@ -4180,6 +4338,7 @@ OO.ui.CapsuleMultiselectWidget.prototype.getMenu = function () { */ OO.ui.CapsuleMultiselectWidget.prototype.onInputFocus = function () { if ( !this.isDisabled() ) { + this.updateInputSize(); this.menu.toggle( true ); } }; @@ -4448,7 +4607,6 @@ OO.ui.CapsuleMultiselectWidget.prototype.setDisabled = function ( disabled ) { * Focus the widget * * @chainable - * @return {OO.ui.CapsuleMultiselectWidget} */ OO.ui.CapsuleMultiselectWidget.prototype.focus = function () { if ( !this.isDisabled() ) { @@ -4457,9 +4615,7 @@ OO.ui.CapsuleMultiselectWidget.prototype.focus = function () { this.popup.toggle( true ); OO.ui.findFocusable( this.popup.$element ).focus(); } else { - this.updateInputSize(); - this.menu.toggle( true ); - this.$input.focus(); + OO.ui.mixin.TabIndexedElement.prototype.focus.call( this ); } } return this; @@ -4598,6 +4754,7 @@ OO.ui.TagItemWidget.prototype.remove = function () { * * @fires navigate * @fires remove + * @param {jQuery.Event} e Key down event * @return {boolean|undefined} false to stop the operation */ OO.ui.TagItemWidget.prototype.onKeyDown = function ( e ) { @@ -4633,15 +4790,6 @@ OO.ui.TagItemWidget.prototype.onKeyDown = function ( e ) { } }; -/** - * Focuses the capsule - */ -OO.ui.TagItemWidget.prototype.focus = function () { - if ( !this.isDisabled() ) { - this.$element.focus(); - } -}; - /** * Select this item * @@ -4850,6 +4998,8 @@ OO.ui.TagMultiselectWidget = function OoUiTagMultiselectWidget( config ) { // having only the $input attached to the DOM this.$content.append( this.input.$input ); } + } else { + this.$content.append( $tabFocus ); } this.setTabIndexedElement( @@ -4974,12 +5124,16 @@ OO.ui.TagMultiselectWidget.prototype.onInputKeyDown = function ( e ) { /** * Respond to input focus event */ -OO.ui.TagMultiselectWidget.prototype.onInputFocus = function () {}; +OO.ui.TagMultiselectWidget.prototype.onInputFocus = function () { + this.$element.addClass( 'oo-ui-tagMultiselectWidget-focus' ); +}; /** * Respond to input blur event */ -OO.ui.TagMultiselectWidget.prototype.onInputBlur = function () {}; +OO.ui.TagMultiselectWidget.prototype.onInputBlur = function () { + this.$element.removeClass( 'oo-ui-tagMultiselectWidget-focus' ); +}; /** * Perform an action after the enter key on the input @@ -5002,7 +5156,7 @@ OO.ui.TagMultiselectWidget.prototype.doInputEnter = function () { * a meta key like 'ctrl' * @return {boolean} Whether to prevent defaults */ -OO.ui.TagMultiselectWidget.prototype.doInputBackspace = function () { +OO.ui.TagMultiselectWidget.prototype.doInputBackspace = function ( e, withMetaKey ) { var items, item; if ( @@ -5013,8 +5167,12 @@ OO.ui.TagMultiselectWidget.prototype.doInputBackspace = function () { // Delete the last item items = this.getItems(); item = items[ items.length - 1 ]; - this.input.setValue( item.getData() ); this.removeItems( [ item ] ); + // If Ctrl/Cmd was pressed, delete item entirely. + // Otherwise put it into the text field for editing. + if ( !withMetaKey ) { + this.input.setValue( item.getData() ); + } return false; } @@ -5024,7 +5182,6 @@ OO.ui.TagMultiselectWidget.prototype.doInputBackspace = function () { * Perform an action after the escape key on the input * * @param {jQuery.Event} e Event data - * @return {boolean} Whether to prevent defaults */ OO.ui.TagMultiselectWidget.prototype.doInputEscape = function () { this.clearInput(); @@ -5039,9 +5196,8 @@ OO.ui.TagMultiselectWidget.prototype.doInputEscape = function () { * @param {string} direction Direction of the movement; forwards or backwards * @param {boolean} [withMetaKey] Whether this key was pressed with * a meta key like 'ctrl' - * @return {boolean} Whether to prevent defaults */ -OO.ui.TagMultiselectWidget.prototype.doInputArrow = function ( direction ) { +OO.ui.TagMultiselectWidget.prototype.doInputArrow = function ( e, direction ) { if ( this.inputPosition === 'inline' && !this.isEmpty() @@ -5058,6 +5214,8 @@ OO.ui.TagMultiselectWidget.prototype.doInputArrow = function ( direction ) { /** * Respond to item select event + * + * @param {OO.ui.TagItemWidget} item Selected item */ OO.ui.TagMultiselectWidget.prototype.onTagSelect = function ( item ) { if ( this.hasInput && this.allowEditTags ) { @@ -5167,10 +5325,6 @@ OO.ui.TagMultiselectWidget.prototype.isDuplicateData = function ( data ) { * @return {boolean} Value is allowed */ OO.ui.TagMultiselectWidget.prototype.isAllowedData = function ( data ) { - if ( this.allowArbitrary ) { - return true; - } - if ( !this.allowDuplicates && this.isDuplicateData( data ) @@ -5178,6 +5332,10 @@ OO.ui.TagMultiselectWidget.prototype.isAllowedData = function ( data ) { return false; } + if ( this.allowArbitrary ) { + return true; + } + // Check with allowed values if ( this.getAllowedValues().some( function ( value ) { @@ -5210,15 +5368,6 @@ OO.ui.TagMultiselectWidget.prototype.addAllowedValue = function ( value ) { } }; -/** - * Focus the widget - */ -OO.ui.TagMultiselectWidget.prototype.focus = function () { - if ( this.hasInput ) { - this.input.focus(); - } -}; - /** * Get the datas of the currently selected items * @@ -5237,7 +5386,7 @@ OO.ui.TagMultiselectWidget.prototype.getValue = function () { /** * Set the value of this widget by datas. * - * @param {string|string[]|Object|Object[]} value An object representing the data + * @param {string|string[]|Object|Object[]} valueObject An object representing the data * and label of the value. If the widget allows arbitrary values, * the items will be added as-is. Otherwise, the data value will * be checked against allowedValues. @@ -5280,7 +5429,9 @@ OO.ui.TagMultiselectWidget.prototype.addTag = function ( data, label ) { newItemWidget = this.createTagItemWidget( data, label ); newItemWidget.toggleValid( isValid ); this.addItems( [ newItemWidget ] ); + return true; } + return false; }; /** @@ -5512,7 +5663,8 @@ OO.ui.TagMultiselectWidget.prototype.isValid = function () { * @mixins OO.ui.mixin.PopupElement * * @param {Object} config Configuration object - * @cfg {jQuery} [$overlay] An overlay for the popup + * @cfg {jQuery} [$overlay] An overlay for the popup. + * See . * @cfg {Object} [popup] Configuration options for the popup * @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 @@ -5566,9 +5718,10 @@ OO.ui.PopupTagMultiselectWidget = function OoUiPopupTagMultiselectWidget( config } // Events + this.on( 'resize', this.popup.updateDimensions.bind( this.popup ) ); this.popup.connect( this, { toggle: 'onPopupToggle' } ); this.$tabIndexed - .on( 'focus', this.focus.bind( this ) ); + .on( 'focus', this.onFocus.bind( this ) ); // Initialize this.$element @@ -5584,17 +5737,11 @@ OO.mixinClass( OO.ui.PopupTagMultiselectWidget, OO.ui.mixin.PopupElement ); /* Methods */ /** - * @inheritdoc + * Focus event handler. + * + * @private */ -OO.ui.PopupTagMultiselectWidget.prototype.focus = function () { - // Since the parent deals with input focus, only - // call the parent method if our input isn't in the - // popup - if ( !this.popupInput ) { - // Parent method - OO.ui.PopupTagMultiselectWidget.parent.prototype.focus.call( this ); - } - +OO.ui.PopupTagMultiselectWidget.prototype.onFocus = function () { this.popup.toggle( true ); }; @@ -5675,7 +5822,8 @@ OO.ui.PopupTagMultiselectWidget.prototype.addTagByPopupValue = function ( data, * @constructor * @param {Object} [config] Configuration object * @cfg {Object} [menu] Configuration object for the menu widget - * @cfg {jQuery} [$overlay] An overlay for the menu + * @cfg {jQuery} [$overlay] An overlay for the menu. + * See . * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }` */ OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) { @@ -5693,7 +5841,7 @@ OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) filterFromInput: !!this.hasInput, $autoCloseIgnore: this.hasInput ? this.input.$element.add( this.$overlay ) : this.$overlay, - $container: this.hasInput && this.inputPosition === 'outline' ? + $floatableContainer: this.hasInput && this.inputPosition === 'outline' ? this.input.$element : this.$element, $overlay: this.$overlay, disabled: this.isDisabled() @@ -5831,13 +5979,13 @@ OO.ui.MenuTagMultiselectWidget.prototype.getMenuVisibleItems = function () { * @return {OO.ui.MenuSelectWidget} Menu widget */ OO.ui.MenuTagMultiselectWidget.prototype.createMenuWidget = function ( menuConfig ) { - return new OO.ui.FloatingMenuSelectWidget( menuConfig ); + return new OO.ui.MenuSelectWidget( menuConfig ); }; /** * Add options to the menu * - * @param {Object[]} options Object defining options + * @param {Object[]} menuOptions Object defining options */ OO.ui.MenuTagMultiselectWidget.prototype.addOptions = function ( menuOptions ) { var widget = this, @@ -5883,18 +6031,6 @@ OO.ui.MenuTagMultiselectWidget.prototype.getAllowedValues = function () { return this.allowedValues.concat( menuDatas ); }; -/** - * @inheritdoc - */ -OO.ui.MenuTagMultiselectWidget.prototype.focus = function () { - // Parent method - OO.ui.MenuTagMultiselectWidget.parent.prototype.focus.call( this ); - - if ( !this.isDisabled() ) { - this.menu.toggle( true ); - } -}; - /** * SelectFileWidgets allow for selecting files, using the HTML5 File API. These * widgets can be configured with {@link OO.ui.mixin.IconElement icons} and {@link @@ -6093,7 +6229,17 @@ OO.ui.SelectFileWidget.prototype.setValue = function ( file ) { * @chainable */ OO.ui.SelectFileWidget.prototype.focus = function () { - this.selectButton.$button[ 0 ].focus(); + this.selectButton.focus(); + return this; +}; + +/** + * Blur the widget. + * + * @chainable + */ +OO.ui.SelectFileWidget.prototype.blur = function () { + this.selectButton.blur(); return this; }; @@ -6546,14 +6692,13 @@ OO.ui.SearchWidget.prototype.getResults = function () { * $( 'body' ).append( numberInput.$element ); * * @class - * @extends OO.ui.Widget + * @extends OO.ui.TextInputWidget * * @constructor * @param {Object} [config] Configuration options - * @cfg {Object} [input] Configuration options to pass to the {@link OO.ui.TextInputWidget text input widget}. * @cfg {Object} [minusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget decrementing button widget}. * @cfg {Object} [plusButton] Configuration options to pass to the {@link OO.ui.ButtonWidget incrementing button widget}. - * @cfg {boolean} [isInteger=false] Whether the field accepts only integer values. + * @cfg {boolean} [allowInteger=false] Whether the field accepts only integer values. * @cfg {number} [min=-Infinity] Minimum allowed value * @cfg {number} [max=Infinity] Maximum allowed value * @cfg {number} [step=1] Delta when using the buttons or up/down arrow keys @@ -6561,9 +6706,12 @@ OO.ui.SearchWidget.prototype.getResults = function () { * @cfg {boolean} [showButtons=true] Whether to show the plus and minus buttons. */ OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) { + var $field = $( '
' ) + .addClass( 'oo-ui-numberInputWidget-field' ); + // Configuration initialization config = $.extend( { - isInteger: false, + allowInteger: false, min: -Infinity, max: Infinity, step: 1, @@ -6571,17 +6719,15 @@ OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) { showButtons: true }, config ); + // For backward compatibility + $.extend( config, config.input ); + this.input = this; + // Parent constructor - OO.ui.NumberInputWidget.parent.call( this, config ); + OO.ui.NumberInputWidget.parent.call( this, $.extend( config, { + type: 'number' + } ) ); - // Properties - this.input = new OO.ui.TextInputWidget( $.extend( - { - disabled: this.isDisabled(), - type: 'number' - }, - config.input - ) ); if ( config.showButtons ) { this.minusButton = new OO.ui.ButtonWidget( $.extend( { @@ -6604,11 +6750,7 @@ OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) { } // Events - this.input.connect( this, { - change: this.emit.bind( this, 'change' ), - enter: this.emit.bind( this, 'enter' ) - } ); - this.input.$input.on( { + this.$input.on( { keydown: this.onKeyDown.bind( this ), 'wheel mousewheel DOMMouseScroll': this.onWheel.bind( this ) } ); @@ -6621,40 +6763,31 @@ OO.ui.NumberInputWidget = function OoUiNumberInputWidget( config ) { } ); } - // Initialization - this.setIsInteger( !!config.isInteger ); - this.setRange( config.min, config.max ); - this.setStep( config.step, config.pageStep ); - - this.$field = $( '
' ).addClass( 'oo-ui-numberInputWidget-field' ) - .append( this.input.$element ); - this.$element.addClass( 'oo-ui-numberInputWidget' ).append( this.$field ); + // Build the field + $field.append( this.$input ); if ( config.showButtons ) { - this.$field + $field .prepend( this.minusButton.$element ) .append( this.plusButton.$element ); - this.$element.addClass( 'oo-ui-numberInputWidget-buttoned' ); } - this.input.setValidation( this.validateNumber.bind( this ) ); -}; -/* Setup */ - -OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.Widget ); + // Initialization + this.setAllowInteger( config.allowInteger || config.isInteger ); + this.setRange( config.min, config.max ); + this.setStep( config.step, config.pageStep ); + // Set the validation method after we set allowInteger and range + // so that it doesn't immediately call setValidityFlag + this.setValidation( this.validateNumber.bind( this ) ); -/* Events */ + this.$element + .addClass( 'oo-ui-numberInputWidget' ) + .toggleClass( 'oo-ui-numberInputWidget-buttoned', config.showButtons ) + .append( $field ); +}; -/** - * A `change` event is emitted when the value of the input changes. - * - * @event change - */ +/* Setup */ -/** - * An `enter` event is emitted when the user presses 'enter' inside the text box. - * - * @event enter - */ +OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.TextInputWidget ); /* Methods */ @@ -6663,19 +6796,23 @@ OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.Widget ); * * @param {boolean} flag */ -OO.ui.NumberInputWidget.prototype.setIsInteger = function ( flag ) { - this.isInteger = !!flag; - this.input.setValidityFlag(); +OO.ui.NumberInputWidget.prototype.setAllowInteger = function ( flag ) { + this.allowInteger = !!flag; + this.setValidityFlag(); }; +// Backward compatibility +OO.ui.NumberInputWidget.prototype.setIsInteger = OO.ui.NumberInputWidget.prototype.setAllowInteger; /** * Get whether only integers are allowed * * @return {boolean} Flag value */ -OO.ui.NumberInputWidget.prototype.getIsInteger = function () { - return this.isInteger; +OO.ui.NumberInputWidget.prototype.getAllowInteger = function () { + return this.allowInteger; }; +// Backward compatibility +OO.ui.NumberInputWidget.prototype.getIsInteger = OO.ui.NumberInputWidget.prototype.getAllowInteger; /** * Set the range of allowed values @@ -6689,7 +6826,7 @@ OO.ui.NumberInputWidget.prototype.setRange = function ( min, max ) { } this.min = min; this.max = max; - this.input.setValidityFlag(); + this.setValidityFlag(); }; /** @@ -6729,31 +6866,13 @@ OO.ui.NumberInputWidget.prototype.getStep = function () { return [ this.step, this.pageStep ]; }; -/** - * Get the current value of the widget - * - * @return {string} - */ -OO.ui.NumberInputWidget.prototype.getValue = function () { - return this.input.getValue(); -}; - /** * Get the current value of the widget as a number * * @return {number} May be NaN, or an invalid number */ OO.ui.NumberInputWidget.prototype.getNumericValue = function () { - return +this.input.getValue(); -}; - -/** - * Set the value of the widget - * - * @param {string} value Invalid values are allowed - */ -OO.ui.NumberInputWidget.prototype.setValue = function ( value ) { - this.input.setValue( value ); + return +this.getValue(); }; /** @@ -6774,7 +6893,7 @@ OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) { } else { n = v + delta; n = Math.max( Math.min( n, this.max ), this.min ); - if ( this.isInteger ) { + if ( this.allowInteger ) { n = Math.round( n ); } } @@ -6783,7 +6902,6 @@ OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) { this.setValue( n ); } }; - /** * Validate input * @@ -6793,11 +6911,15 @@ OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) { */ OO.ui.NumberInputWidget.prototype.validateNumber = function ( value ) { var n = +value; + if ( value === '' ) { + return !this.isRequired(); + } + if ( isNaN( n ) || !isFinite( n ) ) { return false; } - if ( this.isInteger && Math.floor( n ) !== n ) { + if ( this.allowInteger && Math.floor( n ) !== n ) { return false; } @@ -6827,7 +6949,7 @@ OO.ui.NumberInputWidget.prototype.onButtonClick = function ( dir ) { OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) { var delta = 0; - if ( !this.isDisabled() && this.input.$input.is( ':focus' ) ) { + if ( !this.isDisabled() && this.$input.is( ':focus' ) ) { // Standard 'wheel' event if ( event.originalEvent.deltaMode !== undefined ) { this.sawWheelEvent = true; @@ -6892,9 +7014,6 @@ OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) { // Parent method OO.ui.NumberInputWidget.parent.prototype.setDisabled.call( this, disabled ); - if ( this.input ) { - this.input.setDisabled( this.isDisabled() ); - } if ( this.minusButton ) { this.minusButton.setDisabled( this.isDisabled() ); } @@ -6906,3 +7025,5 @@ OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) { }; }( OO ) ); + +//# sourceMappingURL=oojs-ui-widgets.js.map \ No newline at end of file