Merge "userExpLevel test: use a single time()"
[lhc/web/wiklou.git] / resources / lib / oojs-ui / oojs-ui-widgets.js
index 2b2f2ca..58ff9e5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.21.0
+ * OOjs UI v0.21.2
  * 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-11T22:51:05Z
+ * Date: 2017-04-26T01:05:10Z
  */
 ( function ( OO ) {
 
@@ -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 <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
  * @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.
@@ -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 );
@@ -3701,6 +3707,7 @@ OO.ui.CapsuleItemWidget.prototype.focus = function () {
  *  its containing `<div>`. The specified overlay layer is usually on top of
  *  the containing `<div>` and has a larger area. By default, the menu uses
  *  relative positioning.
+ *  See <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
  */
 OO.ui.CapsuleMultiselectWidget = function OoUiCapsuleMultiselectWidget( config ) {
        var $tabFocus;
@@ -3795,6 +3802,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'
                } );
        }
@@ -4713,6 +4721,9 @@ OO.ui.TagItemWidget.prototype.isValid = function () {
  * @constructor
  * @param {Object} config Configuration object
  * @cfg {Object} [input] Configuration options for the input widget
+ * @cfg {OO.ui.InputWidget} [inputWidget] An optional input widget. If given, it will
+ *  replace the input widget used in the TagMultiselectWidget. If not given,
+ *  TagMultiselectWidget creates its own.
  * @cfg {boolean} [inputPosition='inline'] Position of the input. Options are:
  *     - inline: The input is invisible, but exists inside the tag list, so
  *             the user types into the tag groups to add tags.
@@ -4802,14 +4813,17 @@ OO.ui.TagMultiselectWidget = function OoUiTagMultiselectWidget( config ) {
        // Initialize
        this.$element
                .addClass( 'oo-ui-tagMultiselectWidget' )
-               .addClass( 'oo-ui-tagMultiselectWidget-inputPosition-' + this.inputPosition )
                .append( this.$handle );
 
        if ( this.hasInput ) {
-               this.input = new OO.ui.TextInputWidget( $.extend( {
-                       placeholder: config.placeholder,
-                       classes: [ 'oo-ui-tagMultiselectWidget-input' ]
-               }, config.input ) );
+               if ( config.inputWidget ) {
+                       this.input = config.inputWidget;
+               } else {
+                       this.input = new OO.ui.TextInputWidget( $.extend( {
+                               placeholder: config.placeholder,
+                               classes: [ 'oo-ui-tagMultiselectWidget-input' ]
+                       }, config.input ) );
+               }
                this.input.setDisabled( this.isDisabled() );
 
                inputEvents = {
@@ -4828,8 +4842,11 @@ OO.ui.TagMultiselectWidget = function OoUiTagMultiselectWidget( config ) {
                        // in the case the widget is outline so it can
                        // stretch all the way if the widet is wide
                        this.input.$element.css( 'max-width', 'inherit' );
-                       this.$element.append( this.input.$element );
+                       this.$element
+                               .addClass( 'oo-ui-tagMultiselectWidget-outlined' )
+                               .append( this.input.$element );
                } else {
+                       this.$element.addClass( 'oo-ui-tagMultiselectWidget-inlined' );
                        // HACK: When the widget is using 'inline' input, the
                        // behavior needs to only use the $input itself
                        // so we style and size it accordingly (otherwise
@@ -5155,11 +5172,9 @@ OO.ui.TagMultiselectWidget.prototype.isDuplicateData = function ( data ) {
  * Check whether a given value is allowed to be added
  *
  * @param {string|Object} data Requested value
- * @return {boolean} Value exists in the allowed values list
+ * @return {boolean} Value is allowed
  */
 OO.ui.TagMultiselectWidget.prototype.isAllowedData = function ( data ) {
-       var hash = OO.getHash( data );
-
        if ( this.allowArbitrary ) {
                return true;
        }
@@ -5174,7 +5189,7 @@ OO.ui.TagMultiselectWidget.prototype.isAllowedData = function ( data ) {
        // Check with allowed values
        if (
                this.getAllowedValues().some( function ( value ) {
-                       return hash === OO.getHash( value );
+                       return data === value;
                } )
        ) {
                return true;
@@ -5505,7 +5520,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 <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
  * @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
@@ -5559,6 +5575,7 @@ 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 ) );
@@ -5668,7 +5685,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 <https://www.mediawiki.org/wiki/OOjs_UI/Concepts#Overlays>.
  * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }`
  */
 OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) {
@@ -5865,11 +5883,15 @@ OO.ui.MenuTagMultiselectWidget.prototype.getMenu = function () {
 };
 
 /**
- * @inheritdoc
+ * Get the allowed values list
+ *
+ * @return {string[]} Allowed data values
  */
-OO.ui.MenuTagMultiselectWidget.prototype.isAllowedData = function ( data ) {
-       return OO.ui.MenuTagMultiselectWidget.parent.prototype.isAllowedData.call( this, data ) &&
-               !!this.menu.getItemFromData( data );
+OO.ui.MenuTagMultiselectWidget.prototype.getAllowedValues = function () {
+       var menuDatas = this.menu.getItems().map( function ( menuItem ) {
+               return menuItem.getData();
+       } );
+       return this.allowedValues.concat( menuDatas );
 };
 
 /**
@@ -6535,14 +6557,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
@@ -6550,6 +6571,9 @@ 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 = $( '<div>' )
+               .addClass( 'oo-ui-numberInputWidget-field' );
+
        // Configuration initialization
        config = $.extend( {
                isInteger: false,
@@ -6560,17 +6584,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(
                        {
@@ -6593,11 +6615,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 )
        } );
@@ -6610,40 +6628,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 = $( '<div>' ).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.isInteger || config.allowInteger );
+       this.setRange( config.min, config.max );
+       this.setStep( config.step, config.pageStep );
+       // Set the validation method after we set isInteger 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 */
 
@@ -6652,19 +6661,23 @@ OO.inheritClass( OO.ui.NumberInputWidget, OO.ui.Widget );
  *
  * @param {boolean} flag
  */
-OO.ui.NumberInputWidget.prototype.setIsInteger = function ( flag ) {
+OO.ui.NumberInputWidget.prototype.setAllowInteger = function ( flag ) {
        this.isInteger = !!flag;
-       this.input.setValidityFlag();
+       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 () {
+OO.ui.NumberInputWidget.prototype.getAllowInteger = function () {
        return this.isInteger;
 };
+// Backward compatibility
+OO.ui.NumberInputWidget.prototype.getIsInteger = OO.ui.NumberInputWidget.prototype.getAllowInteger;
 
 /**
  * Set the range of allowed values
@@ -6678,7 +6691,7 @@ OO.ui.NumberInputWidget.prototype.setRange = function ( min, max ) {
        }
        this.min = min;
        this.max = max;
-       this.input.setValidityFlag();
+       this.setValidityFlag();
 };
 
 /**
@@ -6718,31 +6731,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();
 };
 
 /**
@@ -6772,7 +6767,6 @@ OO.ui.NumberInputWidget.prototype.adjustValue = function ( delta ) {
                this.setValue( n );
        }
 };
-
 /**
  * Validate input
  *
@@ -6782,6 +6776,10 @@ 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;
        }
@@ -6816,7 +6814,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;
@@ -6881,9 +6879,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() );
        }