Update OOjs UI to v0.23.5
[lhc/web/wiklou.git] / resources / lib / oojs-ui / oojs-ui-core.js
index c92ab4d..738d52f 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.23.0
+ * OOjs UI v0.23.5
  * 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-09-05T21:23:58Z
+ * Date: 2017-10-12T21:25:50Z
  */
 ( function ( OO ) {
 
@@ -581,7 +581,9 @@ OO.ui.mixin = {};
  *  Data can also be specified with the #setData method.
  */
 OO.ui.Element = function OoUiElement( config ) {
-       this.initialConfig = config;
+       if ( OO.ui.isDemo ) {
+               this.initialConfig = config;
+       }
        // Configuration initialization
        config = config || {};
 
@@ -2953,23 +2955,38 @@ OO.ui.mixin.LabelElement.static.label = null;
  *
  * @param {string} text Text
  * @param {string} query Query to find
+ * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
  * @return {jQuery} Text with the first match of the query
  *  sub-string wrapped in highlighted span
  */
-OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query ) {
-       var $result = $( '<span>' ),
+OO.ui.mixin.LabelElement.static.highlightQuery = function ( text, query, compare ) {
+       var i, tLen, qLen,
+               offset = -1,
+               $result = $( '<span>' );
+
+       if ( compare ) {
+               tLen = text.length;
+               qLen = query.length;
+               for ( i = 0; offset === -1 && i <= tLen - qLen; i++ ) {
+                       if ( compare( query, text.slice( i, i + qLen ) ) === 0 ) {
+                               offset = i;
+                       }
+               }
+       } else {
                offset = text.toLowerCase().indexOf( query.toLowerCase() );
+       }
 
        if ( !query.length || offset === -1 ) {
-               return $result.text( text );
-       }
-       $result.append(
-               document.createTextNode( text.slice( 0, offset ) ),
-               $( '<span>' )
-                       .addClass( 'oo-ui-labelElement-label-highlight' )
-                       .text( text.slice( offset, offset + query.length ) ),
-               document.createTextNode( text.slice( offset + query.length ) )
-       );
+               $result.text( text );
+       } else {
+               $result.append(
+                       document.createTextNode( text.slice( 0, offset ) ),
+                       $( '<span>' )
+                               .addClass( 'oo-ui-labelElement-label-highlight' )
+                               .text( text.slice( offset, offset + query.length ) ),
+                       document.createTextNode( text.slice( offset + query.length ) )
+               );
+       }
        return $result.contents();
 };
 
@@ -3023,10 +3040,11 @@ OO.ui.mixin.LabelElement.prototype.setLabel = function ( label ) {
  *
  * @param {string} text Text label to set
  * @param {string} query Substring of text to highlight
+ * @param {Function} [compare] Optional string comparator, e.g. Intl.Collator().compare
  * @chainable
  */
-OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query ) {
-       return this.setLabel( this.constructor.static.highlightQuery( text, query ) );
+OO.ui.mixin.LabelElement.prototype.setHighlightedQuery = function ( text, query, compare ) {
+       return this.setLabel( this.constructor.static.highlightQuery( text, query, compare ) );
 };
 
 /**
@@ -3076,25 +3094,21 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  *
  * - **progressive**:  Progressive styling is applied to convey that the widget will move the user forward in a process.
  * - **destructive**: Destructive styling is applied to convey that the widget will remove something.
- * - **constructive**: Constructive styling is applied to convey that the widget will create something.
+ * - **constructive**: Constructive styling is deprecated since v0.23.2 and equivalent to progressive.
  *
  * The flags affect the appearance of the buttons:
  *
  *     @example
  *     // FlaggedElement is mixed into ButtonWidget to provide styling flags
  *     var button1 = new OO.ui.ButtonWidget( {
- *         label: 'Constructive',
- *         flags: 'constructive'
+ *         label: 'Progressive',
+ *         flags: 'progressive'
  *     } );
  *     var button2 = new OO.ui.ButtonWidget( {
  *         label: 'Destructive',
  *         flags: 'destructive'
  *     } );
- *     var button3 = new OO.ui.ButtonWidget( {
- *         label: 'Progressive',
- *         flags: 'progressive'
- *     } );
- *     $( 'body' ).append( button1.$element, button2.$element, button3.$element );
+ *     $( 'body' ).append( button1.$element, button2.$element );
  *
  * {@link OO.ui.ActionWidget ActionWidgets}, which are a special kind of button that execute an action, use these flags: **primary** and **safe**.
  * Please see the [OOjs UI documentation on MediaWiki] [1] for more information.
@@ -3106,7 +3120,7 @@ OO.ui.mixin.LabelElement.prototype.setLabelContent = function ( label ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'constructive' or 'primary') to apply.
+ * @cfg {string|string[]} [flags] The name or names of the flags (e.g., 'progressive' or 'primary') to apply.
  *  Please see the [OOjs UI documentation on MediaWiki] [2] for more information about available flags.
  *  [2]: https://www.mediawiki.org/wiki/OOjs_UI/Elements/Flagged
  * @cfg {jQuery} [$flagged] The flagged element. By default,
@@ -5346,7 +5360,9 @@ OO.ui.PopupWidget.prototype.computePosition = function () {
        floatablePos = this.$floatableContainer.offset();
        floatablePos[ far ] = floatablePos[ near ] + this.$floatableContainer[ 'outer' + sizeProp ]();
        // Measure where the offsetParent is and compute our position based on that and parentPosition
-       offsetParentPos = this.$element.offsetParent().offset();
+       offsetParentPos = this.$element.offsetParent()[ 0 ] === document.documentElement ?
+               { top: 0, left: 0 } :
+               this.$element.offsetParent().offset();
 
        if ( positionProp === near ) {
                popupPos[ near ] = offsetParentPos[ near ] + parentPosition[ near ];
@@ -5382,7 +5398,9 @@ OO.ui.PopupWidget.prototype.computePosition = function () {
        }
 
        // Check if the popup will go beyond the edge of this.$container
-       containerPos = this.$container.offset();
+       containerPos = this.$container[ 0 ] === document.documentElement ?
+               { top: 0, left: 0 } :
+               this.$container.offset();
        containerPos[ far ] = containerPos[ near ] + this.$container[ 'inner' + sizeProp ]();
        // Take into account how much the popup will move because of the adjustments we're going to make
        popupPos[ near ] += ( positionProp === near ? 1 : -1 ) * positionAdjustment;
@@ -6068,12 +6086,19 @@ OO.ui.SelectWidget.prototype.onFocus = function ( event ) {
                // This widget was focussed, e.g. by the user tabbing to it.
                // The styles for focus state depend on one of the items being selected.
                if ( !this.getSelectedItem() ) {
-                       item = this.getFirstSelectableItem();
+                       item = this.findFirstSelectableItem();
                }
        } else {
-               // One of the options got focussed (and the event bubbled up here).
-               // They can't be tabbed to, but they can be activated using accesskeys.
-               item = this.findTargetItem( event );
+               if ( event.target.tabIndex === -1 ) {
+                       // One of the options got focussed (and the event bubbled up here).
+                       // They can't be tabbed to, but they can be activated using accesskeys.
+                       // OptionWidgets and focusable UI elements inside them have tabindex="-1" set.
+                       item = this.findTargetItem( event );
+               } else {
+                       // There is something actually user-focusable in one of the labels of the options, and the
+                       // user focussed it (e.g. by tabbing to it). Do nothing (especially, don't change the focus).
+                       return;
+               }
        }
 
        if ( item ) {
@@ -6197,7 +6222,7 @@ OO.ui.SelectWidget.prototype.onMouseLeave = function () {
 OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
        var nextItem,
                handled = false,
-               currentItem = this.getHighlightedItem() || this.getSelectedItem();
+               currentItem = this.findHighlightedItem() || this.getSelectedItem();
 
        if ( !this.isDisabled() && this.isVisible() ) {
                switch ( e.keyCode ) {
@@ -6211,13 +6236,13 @@ OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
                        case OO.ui.Keys.UP:
                        case OO.ui.Keys.LEFT:
                                this.clearKeyPressBuffer();
-                               nextItem = this.getRelativeSelectableItem( currentItem, -1 );
+                               nextItem = this.findRelativeSelectableItem( currentItem, -1 );
                                handled = true;
                                break;
                        case OO.ui.Keys.DOWN:
                        case OO.ui.Keys.RIGHT:
                                this.clearKeyPressBuffer();
-                               nextItem = this.getRelativeSelectableItem( currentItem, 1 );
+                               nextItem = this.findRelativeSelectableItem( currentItem, 1 );
                                handled = true;
                                break;
                        case OO.ui.Keys.ESCAPE:
@@ -6323,13 +6348,13 @@ OO.ui.SelectWidget.prototype.onKeyPress = function ( e ) {
        }
        this.keyPressBufferTimer = setTimeout( this.clearKeyPressBuffer.bind( this ), 1500 );
 
-       item = this.getHighlightedItem() || this.getSelectedItem();
+       item = this.findHighlightedItem() || this.getSelectedItem();
 
        if ( this.keyPressBuffer === c ) {
                // Common (if weird) special case: typing "xxxx" will cycle through all
                // the items beginning with "x".
                if ( item ) {
-                       item = this.getRelativeSelectableItem( item, 1 );
+                       item = this.findRelativeSelectableItem( item, 1 );
                }
        } else {
                this.keyPressBuffer += c;
@@ -6337,7 +6362,7 @@ OO.ui.SelectWidget.prototype.onKeyPress = function ( e ) {
 
        filter = this.getItemMatcher( this.keyPressBuffer, false );
        if ( !item || !filter( item ) ) {
-               item = this.getRelativeSelectableItem( item, 1, filter );
+               item = this.findRelativeSelectableItem( item, 1, filter );
        }
        if ( item ) {
                if ( this.isVisible() && item.constructor.static.highlightable ) {
@@ -6447,11 +6472,11 @@ OO.ui.SelectWidget.prototype.getSelectedItem = function () {
 };
 
 /**
- * Get highlighted item.
+ * Find highlighted item.
  *
  * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
  */
-OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
+OO.ui.SelectWidget.prototype.findHighlightedItem = function () {
        var i, len;
 
        for ( i = 0, len = this.items.length; i < len; i++ ) {
@@ -6462,6 +6487,17 @@ OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
        return null;
 };
 
+/**
+ * Get highlighted item.
+ *
+ * @deprecated 0.23.1 Use {@link #findHighlightedItem} instead.
+ * @return {OO.ui.OptionWidget|null} Highlighted item, `null` if no item is highlighted
+ */
+OO.ui.SelectWidget.prototype.getHighlightedItem = function () {
+       OO.ui.warnDeprecation( 'SelectWidget#getHighlightedItem: Deprecated function. Use findHighlightedItem instead. See T76630.' );
+       return this.findHighlightedItem();
+};
+
 /**
  * Toggle pressed state.
  *
@@ -6672,7 +6708,7 @@ OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
 };
 
 /**
- * Get an option by its position relative to the specified item (or to the start of the option array,
+ * Find an option by its position relative to the specified item (or to the start of the option array,
  * if item is `null`). The direction in which to search through the option array is specified with a
  * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
  * `null` if there are no options in the array.
@@ -6683,7 +6719,7 @@ OO.ui.SelectWidget.prototype.chooseItem = function ( item ) {
  *  true. Function takes an OO.ui.OptionWidget and returns a boolean.
  * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
  */
-OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction, filter ) {
+OO.ui.SelectWidget.prototype.findRelativeSelectableItem = function ( item, direction, filter ) {
        var currentIndex, nextIndex, i,
                increase = direction > 0 ? 1 : -1,
                len = this.items.length;
@@ -6710,14 +6746,44 @@ OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direct
        return null;
 };
 
+/**
+ * Get an option by its position relative to the specified item (or to the start of the option array,
+ * if item is `null`). The direction in which to search through the option array is specified with a
+ * number: -1 for reverse (the default) or 1 for forward. The method will return an option, or
+ * `null` if there are no options in the array.
+ *
+ * @deprecated 0.23.1 Use {@link #findRelativeSelectableItem} instead
+ * @param {OO.ui.OptionWidget|null} item Item to describe the start position, or `null` to start at the beginning of the array.
+ * @param {number} direction Direction to move in: -1 to move backward, 1 to move forward
+ * @param {Function} [filter] Only consider items for which this function returns
+ *  true. Function takes an OO.ui.OptionWidget and returns a boolean.
+ * @return {OO.ui.OptionWidget|null} Item at position, `null` if there are no items in the select
+ */
+OO.ui.SelectWidget.prototype.getRelativeSelectableItem = function ( item, direction, filter ) {
+       OO.ui.warnDeprecation( 'SelectWidget#getRelativeSelectableItem: Deprecated function. Use findRelativeSelectableItem instead. See T76630.' );
+       return this.findRelativeSelectableItem( item, direction, filter );
+};
+
+/**
+ * Find the next selectable item or `null` if there are no selectable items.
+ * Disabled options and menu-section markers and breaks are not selectable.
+ *
+ * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
+ */
+OO.ui.SelectWidget.prototype.findFirstSelectableItem = function () {
+       return this.findRelativeSelectableItem( null, 1 );
+};
+
 /**
  * Get the next selectable item or `null` if there are no selectable items.
  * Disabled options and menu-section markers and breaks are not selectable.
  *
+ * @deprecated 0.23.1 Use {@link OO.ui.SelectWidget#findFirstSelectableItem} instead.
  * @return {OO.ui.OptionWidget|null} Item, `null` if there aren't any selectable items
  */
 OO.ui.SelectWidget.prototype.getFirstSelectableItem = function () {
-       return this.getRelativeSelectableItem( null, 1 );
+       OO.ui.warnDeprecation( 'SelectWidget#getFirstSelectableItem: Deprecated function. Use findFirstSelectableItem instead. See T76630.' );
+       return this.findFirstSelectableItem();
 };
 
 /**
@@ -7075,7 +7141,7 @@ OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) {
  * @inheritdoc
  */
 OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) {
-       var currentItem = this.getHighlightedItem() || this.getSelectedItem();
+       var currentItem = this.findHighlightedItem() || this.getSelectedItem();
 
        if ( !this.isDisabled() && this.isVisible() ) {
                switch ( e.keyCode ) {
@@ -8930,7 +8996,7 @@ OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) {
        value = this.cleanUpValue( value );
        // Only allow setting values that are actually present in the dropdown
        selected = this.dropdownWidget.getMenu().getItemFromData( value ) ||
-               this.dropdownWidget.getMenu().getFirstSelectableItem();
+               this.dropdownWidget.getMenu().findFirstSelectableItem();
        this.dropdownWidget.getMenu().selectItem( selected );
        value = selected ? selected.getData() : '';
        OO.ui.DropdownInputWidget.parent.prototype.setValue.call( this, value );
@@ -9888,7 +9954,7 @@ OO.ui.TextInputWidget.prototype.getInputElement = function ( config ) {
  *
  * @param {Object} config Configuration options
  * @return {string|null}
- * @private
+ * @protected
  */
 OO.ui.TextInputWidget.prototype.getSaneType = function ( config ) {
        var allowedTypes = [
@@ -10216,10 +10282,6 @@ OO.ui.SearchInputWidget = function OoUiSearchInputWidget( config ) {
                icon: 'search'
        }, config );
 
-       // Set type to text so that TextInputWidget doesn't
-       // get stuck in an infinite loop.
-       config.type = 'text';
-
        // Parent constructor
        OO.ui.SearchInputWidget.parent.call( this, config );
 
@@ -10229,7 +10291,6 @@ OO.ui.SearchInputWidget = function OoUiSearchInputWidget( config ) {
        } );
 
        // Initialization
-       this.$element.addClass( 'oo-ui-textInputWidget-type-search' );
        this.updateSearchIndicator();
        this.connect( this, {
                disable: 'onDisable'
@@ -10246,8 +10307,8 @@ OO.inheritClass( OO.ui.SearchInputWidget, OO.ui.TextInputWidget );
  * @inheritdoc
  * @protected
  */
-OO.ui.SearchInputWidget.prototype.getInputElement = function () {
-       return $( '<input>' ).attr( 'type', 'search' );
+OO.ui.SearchInputWidget.prototype.getSaneType = function () {
+       return 'search';
 };
 
 /**
@@ -10675,7 +10736,7 @@ OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
        var match = this.menu.getItemFromData( value );
 
        this.menu.selectItem( match );
-       if ( this.menu.getHighlightedItem() ) {
+       if ( this.menu.findHighlightedItem() ) {
                this.menu.highlightItem( match );
        }
 
@@ -10723,7 +10784,7 @@ OO.ui.ComboBoxInputWidget.prototype.onMenuChoose = function ( item ) {
 OO.ui.ComboBoxInputWidget.prototype.onMenuItemsChange = function () {
        var match = this.menu.getItemFromData( this.getValue() );
        this.menu.selectItem( match );
-       if ( this.menu.getHighlightedItem() ) {
+       if ( this.menu.findHighlightedItem() ) {
                this.menu.highlightItem( match );
        }
        this.$element.toggleClass( 'oo-ui-comboBoxInputWidget-empty', this.menu.isEmpty() );