/*!
- * 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 ) {
* 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();
}
*
* @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 ) {
* @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 ) {
* @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 ) ) {
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 */
/**
* 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 */
/**
*
* @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;
*
* @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;
*
* @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;
*
* @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() ) {
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:
*
* @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;
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 } );
}
this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
this.togglePositioning( false );
this.toggleClipping( false );
+ this.lastHighlightedItem = null;
}
}
*
* @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 ) {
*
* @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 (
*
* @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 ) {
*
* @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 ) {
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;
*
* @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;
*
* @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;
};
*
* @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 &&