Merge "Corrected grammatical error."
[lhc/web/wiklou.git] / resources / lib / ooui / oojs-ui-core.js
index 2bb08e0..a51ba74 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.31.3
+ * OOUI v0.31.6
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-04-04T19:10:48Z
+ * Date: 2019-05-08T10:08:36Z
  */
 ( function ( OO ) {
 
@@ -1283,75 +1283,107 @@ OO.ui.Element.static.getClosestScrollableContainer = function ( el, dimension )
  * Scroll element into view.
  *
  * @static
- * @param {HTMLElement} el Element to scroll into view
+ * @param {HTMLElement|Object} elOrPosition Element to scroll into view
  * @param {Object} [config] Configuration options
+ * @param {string} [config.animate=true] Animate to the new scroll offset.
  * @param {string} [config.duration='fast'] jQuery animation duration value
  * @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
  *  to scroll in both directions
+ * @param {Object} [config.padding] Additional padding on the container to scroll past.
+ *  Object containing any of 'top', 'bottom', 'left', or 'right' as numbers.
+ * @param {Object} [config.scrollContainer] Scroll container. Defaults to
+ *  getClosestScrollableContainer of the element.
  * @return {jQuery.Promise} Promise which resolves when the scroll is complete
  */
-OO.ui.Element.static.scrollIntoView = function ( el, config ) {
-       var position, animations, container, $container, elementDimensions, containerDimensions,
-               $window,
+OO.ui.Element.static.scrollIntoView = function ( elOrPosition, config ) {
+       var position, animations, container, $container, elementPosition, containerDimensions,
+               $window, padding, animate, method,
                deferred = $.Deferred();
 
        // Configuration initialization
        config = config || {};
 
+       padding = $.extend( {
+               top: 0,
+               bottom: 0,
+               left: 0,
+               right: 0
+       }, config.padding );
+
+       animate = config.animate !== false;
+
        animations = {};
-       container = this.getClosestScrollableContainer( el, config.direction );
+       elementPosition = elOrPosition instanceof HTMLElement ?
+               this.getDimensions( elOrPosition ).rect :
+               elOrPosition;
+       container = config.scrollContainer || (
+               elOrPosition instanceof HTMLElement ?
+                       this.getClosestScrollableContainer( elOrPosition, config.direction ) :
+                       // No scrollContainer or element
+                       this.getClosestScrollableContainer( document.body )
+       );
        $container = $( container );
-       elementDimensions = this.getDimensions( el );
        containerDimensions = this.getDimensions( container );
-       $window = $( this.getWindow( el ) );
+       $window = $( this.getWindow( container ) );
 
        // Compute the element's position relative to the container
        if ( $container.is( 'html, body' ) ) {
                // If the scrollable container is the root, this is easy
                position = {
-                       top: elementDimensions.rect.top,
-                       bottom: $window.innerHeight() - elementDimensions.rect.bottom,
-                       left: elementDimensions.rect.left,
-                       right: $window.innerWidth() - elementDimensions.rect.right
+                       top: elementPosition.top,
+                       bottom: $window.innerHeight() - elementPosition.bottom,
+                       left: elementPosition.left,
+                       right: $window.innerWidth() - elementPosition.right
                };
        } else {
                // Otherwise, we have to subtract el's coordinates from container's coordinates
                position = {
-                       top: elementDimensions.rect.top -
+                       top: elementPosition.top -
                                ( containerDimensions.rect.top + containerDimensions.borders.top ),
                        bottom: containerDimensions.rect.bottom - containerDimensions.borders.bottom -
-                               containerDimensions.scrollbar.bottom - elementDimensions.rect.bottom,
-                       left: elementDimensions.rect.left -
+                               containerDimensions.scrollbar.bottom - elementPosition.bottom,
+                       left: elementPosition.left -
                                ( containerDimensions.rect.left + containerDimensions.borders.left ),
                        right: containerDimensions.rect.right - containerDimensions.borders.right -
-                               containerDimensions.scrollbar.right - elementDimensions.rect.right
+                               containerDimensions.scrollbar.right - elementPosition.right
                };
        }
 
        if ( !config.direction || config.direction === 'y' ) {
-               if ( position.top < 0 ) {
-                       animations.scrollTop = containerDimensions.scroll.top + position.top;
-               } else if ( position.top > 0 && position.bottom < 0 ) {
+               if ( position.top < padding.top ) {
+                       animations.scrollTop = containerDimensions.scroll.top + position.top - padding.top;
+               } else if ( position.bottom < padding.bottom ) {
                        animations.scrollTop = containerDimensions.scroll.top +
-                               Math.min( position.top, -position.bottom );
+                               // Scroll the bottom into view, but not at the expense
+                               // of scrolling the top out of view
+                               Math.min( position.top - padding.top, -position.bottom + padding.bottom );
                }
        }
        if ( !config.direction || config.direction === 'x' ) {
-               if ( position.left < 0 ) {
-                       animations.scrollLeft = containerDimensions.scroll.left + position.left;
-               } else if ( position.left > 0 && position.right < 0 ) {
+               if ( position.left < padding.left ) {
+                       animations.scrollLeft = containerDimensions.scroll.left + position.left - padding.left;
+               } else if ( position.right < padding.right ) {
                        animations.scrollLeft = containerDimensions.scroll.left +
-                               Math.min( position.left, -position.right );
+                               // Scroll the right into view, but not at the expense
+                               // of scrolling the left out of view
+                               Math.min( position.left - padding.left, -position.right + padding.right );
                }
        }
        if ( !$.isEmptyObject( animations ) ) {
-               // eslint-disable-next-line no-jquery/no-animate
-               $container.stop( true ).animate( animations, config.duration === undefined ?
-                       'fast' : config.duration );
-               $container.queue( function ( next ) {
+               if ( animate ) {
+                       // eslint-disable-next-line no-jquery/no-animate
+                       $container.stop( true ).animate( animations, config.duration === undefined ? 'fast' : config.duration );
+                       $container.queue( function ( next ) {
+                               deferred.resolve();
+                               next();
+                       } );
+               } else {
+                       $container.stop( true );
+                       for ( method in animations ) {
+                               $container[ method ]( animations[ method ] );
+                       }
                        deferred.resolve();
-                       next();
-               } );
+               }
        } else {
                deferred.resolve();
        }
@@ -2253,7 +2285,7 @@ OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
  *
  * @protected
  * @param {jQuery.Event} e Mouse down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
        if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
@@ -2290,7 +2322,7 @@ OO.ui.mixin.ButtonElement.prototype.onDocumentMouseUp = function ( e ) {
  * @protected
  * @param {jQuery.Event} e Mouse click event
  * @fires click
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
        if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
@@ -2337,7 +2369,7 @@ OO.ui.mixin.ButtonElement.prototype.onDocumentKeyUp = function ( e ) {
  * @protected
  * @param {jQuery.Event} e Key press event
  * @fires click
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.mixin.ButtonElement.prototype.onKeyPress = function ( e ) {
        if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
@@ -3232,10 +3264,14 @@ OO.ui.mixin.FlaggedElement = function OoUiMixinFlaggedElement( config ) {
        this.$flagged = null;
 
        // Initialization
-       this.setFlags( config.flags );
+       this.setFlags( config.flags || this.constructor.static.flags );
        this.setFlaggedElement( config.$flagged || this.$element );
 };
 
+/* Setup */
+
+OO.initClass( OO.ui.mixin.FlaggedElement );
+
 /* Events */
 
 /**
@@ -3248,6 +3284,17 @@ OO.ui.mixin.FlaggedElement = function OoUiMixinFlaggedElement( config ) {
  * that the flag was added, `false` that the flag was removed.
  */
 
+/* Static Properties */
+
+/**
+ * Initial value to pass to setFlags if no value is provided in config.
+ *
+ * @static
+ * @inheritable
+ * @property {string|string[]|Object.<string, boolean>}
+ */
+OO.ui.mixin.FlaggedElement.static.flags = null;
+
 /* Methods */
 
 /**
@@ -6580,7 +6627,7 @@ OO.ui.SelectWidget.prototype.onFocus = function ( event ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
        var item;
@@ -6603,7 +6650,7 @@ OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
  *
  * @private
  * @param {MouseEvent} e Mouse up event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onDocumentMouseUp = function ( e ) {
        var item;
@@ -6650,7 +6697,7 @@ OO.ui.SelectWidget.prototype.onDocumentMouseMove = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse over event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
        var item;
@@ -6669,7 +6716,7 @@ OO.ui.SelectWidget.prototype.onMouseOver = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse over event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onMouseLeave = function () {
        if ( !this.isDisabled() ) {
@@ -6702,13 +6749,15 @@ OO.ui.SelectWidget.prototype.onDocumentKeyDown = function ( e ) {
                        case OO.ui.Keys.UP:
                        case OO.ui.Keys.LEFT:
                                this.clearKeyPressBuffer();
-                               nextItem = currentItem ? this.findRelativeSelectableItem( currentItem, -1 ) : firstItem;
+                               nextItem = currentItem ?
+                                       this.findRelativeSelectableItem( currentItem, -1 ) : firstItem;
                                handled = true;
                                break;
                        case OO.ui.Keys.DOWN:
                        case OO.ui.Keys.RIGHT:
                                this.clearKeyPressBuffer();
-                               nextItem = currentItem ? this.findRelativeSelectableItem( currentItem, 1 ) : firstItem;
+                               nextItem = currentItem ?
+                                       this.findRelativeSelectableItem( currentItem, 1 ) : firstItem;
                                handled = true;
                                break;
                        case OO.ui.Keys.ESCAPE:
@@ -6792,7 +6841,7 @@ OO.ui.SelectWidget.prototype.clearKeyPressBuffer = function () {
  *
  * @protected
  * @param {KeyboardEvent} e Key press event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectWidget.prototype.onDocumentKeyPress = function ( e ) {
        var c, filter, item;
@@ -7976,8 +8025,8 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
                        selectedItem = this.findSelectedItem();
                        if ( !this.multiselect && selectedItem ) {
                                // TODO: Verify if this is even needed; This is already done on highlight changes
-                               // in SelectWidget#highlightItem, so we should just need to highlight the item we need to
-                               // highlight here and not bother with attr or checking selections.
+                               // in SelectWidget#highlightItem, so we should just need to highlight the item
+                               // we need to highlight here and not bother with attr or checking selections.
                                this.$focusOwner.attr( 'aria-activedescendant', selectedItem.getElementId() );
                                selectedItem.scrollElementIntoView( { duration: 0 } );
                        }
@@ -7996,6 +8045,7 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
                        this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
                        this.togglePositioning( false );
                        this.toggleClipping( false );
+                       this.lastHighlightedItem = null;
                }
        }
 
@@ -8183,7 +8233,7 @@ OO.ui.DropdownWidget.prototype.onMenuToggle = function ( isVisible ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse click event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
        if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
@@ -8197,7 +8247,7 @@ OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Key down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.DropdownWidget.prototype.onKeyDown = function ( e ) {
        if (
@@ -10647,7 +10697,7 @@ OO.ui.TextInputWidget.static.validationPatterns = {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
        if ( e.which === OO.ui.MouseButtons.LEFT ) {
@@ -10661,7 +10711,7 @@ OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
  *
  * @private
  * @param {jQuery.Event} e Mouse down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
        if ( e.which === OO.ui.MouseButtons.LEFT ) {
@@ -12020,7 +12070,7 @@ OO.ui.FieldLayout.prototype.makeMessage = function ( kind, text ) {
        var $listItem, $icon, message;
        $listItem = $( '<li>' );
        if ( kind === 'error' ) {
-               $icon = new OO.ui.IconWidget( { icon: 'alert', flags: [ 'error' ] } ).$element;
+               $icon = new OO.ui.IconWidget( { icon: 'error', flags: [ 'error' ] } ).$element;
                $listItem.attr( 'role', 'alert' );
        } else if ( kind === 'warning' ) {
                $icon = new OO.ui.IconWidget( { icon: 'alert', flags: [ 'warning' ] } ).$element;
@@ -13002,12 +13052,16 @@ OO.ui.NumberInputWidget.prototype.onButtonClick = function ( dir ) {
  *
  * @private
  * @param {jQuery.Event} event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
        var delta = 0;
 
-       if ( !this.isDisabled() && this.$input.is( ':focus' ) ) {
+       if ( this.isDisabled() || this.isReadOnly() ) {
+               return;
+       }
+
+       if ( this.$input.is( ':focus' ) ) {
                // Standard 'wheel' event
                if ( event.originalEvent.deltaMode !== undefined ) {
                        this.sawWheelEvent = true;
@@ -13045,41 +13099,64 @@ OO.ui.NumberInputWidget.prototype.onWheel = function ( event ) {
  *
  * @private
  * @param {jQuery.Event} e Key down event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.NumberInputWidget.prototype.onKeyDown = function ( e ) {
-       if ( !this.isDisabled() ) {
-               switch ( e.which ) {
-                       case OO.ui.Keys.UP:
-                               this.adjustValue( this.buttonStep );
-                               return false;
-                       case OO.ui.Keys.DOWN:
-                               this.adjustValue( -this.buttonStep );
-                               return false;
-                       case OO.ui.Keys.PAGEUP:
-                               this.adjustValue( this.pageStep );
-                               return false;
-                       case OO.ui.Keys.PAGEDOWN:
-                               this.adjustValue( -this.pageStep );
-                               return false;
-               }
+       if ( this.isDisabled() || this.isReadOnly() ) {
+               return;
+       }
+
+       switch ( e.which ) {
+               case OO.ui.Keys.UP:
+                       this.adjustValue( this.buttonStep );
+                       return false;
+               case OO.ui.Keys.DOWN:
+                       this.adjustValue( -this.buttonStep );
+                       return false;
+               case OO.ui.Keys.PAGEUP:
+                       this.adjustValue( this.pageStep );
+                       return false;
+               case OO.ui.Keys.PAGEDOWN:
+                       this.adjustValue( -this.pageStep );
+                       return false;
        }
 };
 
 /**
- * @inheritdoc
+ * Update the disabled state of the controls
+ *
+ * @chainable
+ * @protected
+ * @return {OO.ui.NumberInputWidget} The widget, for chaining
  */
-OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) {
-       // Parent method
-       OO.ui.NumberInputWidget.parent.prototype.setDisabled.call( this, disabled );
-
+OO.ui.NumberInputWidget.prototype.updateControlsDisabled = function () {
+       var disabled = this.isDisabled() || this.isReadOnly();
        if ( this.minusButton ) {
-               this.minusButton.setDisabled( this.isDisabled() );
+               this.minusButton.setDisabled( disabled );
        }
        if ( this.plusButton ) {
-               this.plusButton.setDisabled( this.isDisabled() );
+               this.plusButton.setDisabled( disabled );
        }
+       return this;
+};
 
+/**
+ * @inheritdoc
+ */
+OO.ui.NumberInputWidget.prototype.setDisabled = function ( disabled ) {
+       // Parent method
+       OO.ui.NumberInputWidget.parent.prototype.setDisabled.call( this, disabled );
+       this.updateControlsDisabled();
+       return this;
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.NumberInputWidget.prototype.setReadOnly = function () {
+       // Parent method
+       OO.ui.NumberInputWidget.parent.prototype.setReadOnly.apply( this, arguments );
+       this.updateControlsDisabled();
        return this;
 };
 
@@ -13319,7 +13396,7 @@ OO.ui.SelectFileInputWidget.prototype.onInfoChange = function ( value ) {
  *
  * @private
  * @param {jQuery.Event} e Key press event
- * @return {undefined/boolean} False to prevent default if event is handled
+ * @return {undefined|boolean} False to prevent default if event is handled
  */
 OO.ui.SelectFileInputWidget.prototype.onKeyPress = function ( e ) {
        if ( !this.isDisabled() && this.$input &&