summaryrefslogtreecommitdiff
path: root/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/oojs/oojs-ui/src/widgets/PopupWidget.js')
-rw-r--r--vendor/oojs/oojs-ui/src/widgets/PopupWidget.js395
1 files changed, 0 insertions, 395 deletions
diff --git a/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js b/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js
deleted file mode 100644
index 0b1f4ca6..00000000
--- a/vendor/oojs/oojs-ui/src/widgets/PopupWidget.js
+++ /dev/null
@@ -1,395 +0,0 @@
-/**
- * PopupWidget is a container for content. The popup is overlaid and positioned absolutely.
- * By default, each popup has an anchor that points toward its origin.
- * Please see the [OOjs UI documentation on Mediawiki] [1] for more information and examples.
- *
- * @example
- * // A popup widget.
- * var popup = new OO.ui.PopupWidget( {
- * $content: $( '<p>Hi there!</p>' ),
- * padded: true,
- * width: 300
- * } );
- *
- * $( 'body' ).append( popup.$element );
- * // To display the popup, toggle the visibility to 'true'.
- * popup.toggle( true );
- *
- * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.LabelElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {number} [width=320] Width of popup in pixels
- * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height.
- * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup
- * @cfg {string} [align='center'] Alignment of the popup: `center`, `force-left`, `force-right`, `backwards` or `forwards`.
- * If the popup is forced-left the popup body is leaning towards the left. For force-right alignment, the body of the
- * popup is leaning towards the right of the screen.
- * Using 'backwards' is a logical direction which will result in the popup leaning towards the beginning of the sentence
- * in the given language, which means it will flip to the correct positioning in right-to-left languages.
- * Using 'forward' will also result in a logical alignment where the body of the popup leans towards the end of the
- * sentence in the given language.
- * @cfg {jQuery} [$container] Constrain the popup to the boundaries of the specified container.
- * See the [OOjs UI docs on MediaWiki][3] for an example.
- * [3]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#containerExample
- * @cfg {number} [containerPadding=10] Padding between the popup and its container, specified as a number of pixels.
- * @cfg {jQuery} [$content] Content to append to the popup's body
- * @cfg {boolean} [autoClose=false] Automatically close the popup when it loses focus.
- * @cfg {jQuery} [$autoCloseIgnore] Elements that will not close the popup when clicked.
- * This config option is only relevant if #autoClose is set to `true`. See the [OOjs UI docs on MediaWiki][2]
- * for an example.
- * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Popups#autocloseExample
- * @cfg {boolean} [head] Show a popup header that contains a #label (if specified) and close
- * button.
- * @cfg {boolean} [padded] Add padding to the popup's body
- */
-OO.ui.PopupWidget = function OoUiPopupWidget( config ) {
- // Configuration initialization
- config = config || {};
-
- // Parent constructor
- OO.ui.PopupWidget.super.call( this, config );
-
- // Properties (must be set before ClippableElement constructor call)
- this.$body = $( '<div>' );
-
- // Mixin constructors
- OO.ui.LabelElement.call( this, config );
- OO.ui.ClippableElement.call( this, $.extend( {}, config, { $clippable: this.$body } ) );
-
- // Properties
- this.$popup = $( '<div>' );
- this.$head = $( '<div>' );
- this.$anchor = $( '<div>' );
- // If undefined, will be computed lazily in updateDimensions()
- this.$container = config.$container;
- this.containerPadding = config.containerPadding !== undefined ? config.containerPadding : 10;
- this.autoClose = !!config.autoClose;
- this.$autoCloseIgnore = config.$autoCloseIgnore;
- this.transitionTimeout = null;
- this.anchor = null;
- this.width = config.width !== undefined ? config.width : 320;
- this.height = config.height !== undefined ? config.height : null;
- this.setAlignment( config.align );
- this.closeButton = new OO.ui.ButtonWidget( { framed: false, icon: 'close' } );
- this.onMouseDownHandler = this.onMouseDown.bind( this );
- this.onDocumentKeyDownHandler = this.onDocumentKeyDown.bind( this );
-
- // Events
- this.closeButton.connect( this, { click: 'onCloseButtonClick' } );
-
- // Initialization
- this.toggleAnchor( config.anchor === undefined || config.anchor );
- this.$body.addClass( 'oo-ui-popupWidget-body' );
- this.$anchor.addClass( 'oo-ui-popupWidget-anchor' );
- this.$head
- .addClass( 'oo-ui-popupWidget-head' )
- .append( this.$label, this.closeButton.$element );
- if ( !config.head ) {
- this.$head.addClass( 'oo-ui-element-hidden' );
- }
- this.$popup
- .addClass( 'oo-ui-popupWidget-popup' )
- .append( this.$head, this.$body );
- this.$element
- .addClass( 'oo-ui-popupWidget' )
- .append( this.$popup, this.$anchor );
- // Move content, which was added to #$element by OO.ui.Widget, to the body
- if ( config.$content instanceof jQuery ) {
- this.$body.append( config.$content );
- }
- if ( config.padded ) {
- this.$body.addClass( 'oo-ui-popupWidget-body-padded' );
- }
-
- // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
- // that reference properties not initialized at that time of parent class construction
- // TODO: Find a better way to handle post-constructor setup
- this.visible = false;
- this.$element.addClass( 'oo-ui-element-hidden' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.PopupWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.PopupWidget, OO.ui.LabelElement );
-OO.mixinClass( OO.ui.PopupWidget, OO.ui.ClippableElement );
-
-/* Methods */
-
-/**
- * Handles mouse down events.
- *
- * @private
- * @param {MouseEvent} e Mouse down event
- */
-OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) {
- if (
- this.isVisible() &&
- !$.contains( this.$element[ 0 ], e.target ) &&
- ( !this.$autoCloseIgnore || !this.$autoCloseIgnore.has( e.target ).length )
- ) {
- this.toggle( false );
- }
-};
-
-/**
- * Bind mouse down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
- // Capture clicks outside popup
- this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
-};
-
-/**
- * Handles close button click events.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.onCloseButtonClick = function () {
- if ( this.isVisible() ) {
- this.toggle( false );
- }
-};
-
-/**
- * Unbind mouse down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
- this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
-};
-
-/**
- * Handles key down events.
- *
- * @private
- * @param {KeyboardEvent} e Key down event
- */
-OO.ui.PopupWidget.prototype.onDocumentKeyDown = function ( e ) {
- if (
- e.which === OO.ui.Keys.ESCAPE &&
- this.isVisible()
- ) {
- this.toggle( false );
- e.preventDefault();
- e.stopPropagation();
- }
-};
-
-/**
- * Bind key down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
- this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
-};
-
-/**
- * Unbind key down listener.
- *
- * @private
- */
-OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
- this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
-};
-
-/**
- * Show, hide, or toggle the visibility of the anchor.
- *
- * @param {boolean} [show] Show anchor, omit to toggle
- */
-OO.ui.PopupWidget.prototype.toggleAnchor = function ( show ) {
- show = show === undefined ? !this.anchored : !!show;
-
- if ( this.anchored !== show ) {
- if ( show ) {
- this.$element.addClass( 'oo-ui-popupWidget-anchored' );
- } else {
- this.$element.removeClass( 'oo-ui-popupWidget-anchored' );
- }
- this.anchored = show;
- }
-};
-
-/**
- * Check if the anchor is visible.
- *
- * @return {boolean} Anchor is visible
- */
-OO.ui.PopupWidget.prototype.hasAnchor = function () {
- return this.anchor;
-};
-
-/**
- * @inheritdoc
- */
-OO.ui.PopupWidget.prototype.toggle = function ( show ) {
- show = show === undefined ? !this.isVisible() : !!show;
-
- var change = show !== this.isVisible();
-
- // Parent method
- OO.ui.PopupWidget.super.prototype.toggle.call( this, show );
-
- if ( change ) {
- if ( show ) {
- if ( this.autoClose ) {
- this.bindMouseDownListener();
- this.bindKeyDownListener();
- }
- this.updateDimensions();
- this.toggleClipping( true );
- } else {
- this.toggleClipping( false );
- if ( this.autoClose ) {
- this.unbindMouseDownListener();
- this.unbindKeyDownListener();
- }
- }
- }
-
- return this;
-};
-
-/**
- * Set the size of the popup.
- *
- * Changing the size may also change the popup's position depending on the alignment.
- *
- * @param {number} width Width in pixels
- * @param {number} height Height in pixels
- * @param {boolean} [transition=false] Use a smooth transition
- * @chainable
- */
-OO.ui.PopupWidget.prototype.setSize = function ( width, height, transition ) {
- this.width = width;
- this.height = height !== undefined ? height : null;
- if ( this.isVisible() ) {
- this.updateDimensions( transition );
- }
-};
-
-/**
- * Update the size and position.
- *
- * Only use this to keep the popup properly anchored. Use #setSize to change the size, and this will
- * be called automatically.
- *
- * @param {boolean} [transition=false] Use a smooth transition
- * @chainable
- */
-OO.ui.PopupWidget.prototype.updateDimensions = function ( transition ) {
- var popupOffset, originOffset, containerLeft, containerWidth, containerRight,
- popupLeft, popupRight, overlapLeft, overlapRight, anchorWidth,
- align = this.align,
- widget = this;
-
- if ( !this.$container ) {
- // Lazy-initialize $container if not specified in constructor
- this.$container = $( this.getClosestScrollableElementContainer() );
- }
-
- // Set height and width before measuring things, since it might cause our measurements
- // to change (e.g. due to scrollbars appearing or disappearing)
- this.$popup.css( {
- width: this.width,
- height: this.height !== null ? this.height : 'auto'
- } );
-
- // If we are in RTL, we need to flip the alignment, unless it is center
- if ( align === 'forwards' || align === 'backwards' ) {
- if ( this.$container.css( 'direction' ) === 'rtl' ) {
- align = ( { forwards: 'force-left', backwards: 'force-right' } )[ this.align ];
- } else {
- align = ( { forwards: 'force-right', backwards: 'force-left' } )[ this.align ];
- }
-
- }
-
- // Compute initial popupOffset based on alignment
- popupOffset = this.width * ( { 'force-left': -1, center: -0.5, 'force-right': 0 } )[ align ];
-
- // Figure out if this will cause the popup to go beyond the edge of the container
- originOffset = this.$element.offset().left;
- containerLeft = this.$container.offset().left;
- containerWidth = this.$container.innerWidth();
- containerRight = containerLeft + containerWidth;
- popupLeft = popupOffset - this.containerPadding;
- popupRight = popupOffset + this.containerPadding + this.width + this.containerPadding;
- overlapLeft = ( originOffset + popupLeft ) - containerLeft;
- overlapRight = containerRight - ( originOffset + popupRight );
-
- // Adjust offset to make the popup not go beyond the edge, if needed
- if ( overlapRight < 0 ) {
- popupOffset += overlapRight;
- } else if ( overlapLeft < 0 ) {
- popupOffset -= overlapLeft;
- }
-
- // Adjust offset to avoid anchor being rendered too close to the edge
- // $anchor.width() doesn't work with the pure CSS anchor (returns 0)
- // TODO: Find a measurement that works for CSS anchors and image anchors
- anchorWidth = this.$anchor[ 0 ].scrollWidth * 2;
- if ( popupOffset + this.width < anchorWidth ) {
- popupOffset = anchorWidth - this.width;
- } else if ( -popupOffset < anchorWidth ) {
- popupOffset = -anchorWidth;
- }
-
- // Prevent transition from being interrupted
- clearTimeout( this.transitionTimeout );
- if ( transition ) {
- // Enable transition
- this.$element.addClass( 'oo-ui-popupWidget-transitioning' );
- }
-
- // Position body relative to anchor
- this.$popup.css( 'margin-left', popupOffset );
-
- if ( transition ) {
- // Prevent transitioning after transition is complete
- this.transitionTimeout = setTimeout( function () {
- widget.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
- }, 200 );
- } else {
- // Prevent transitioning immediately
- this.$element.removeClass( 'oo-ui-popupWidget-transitioning' );
- }
-
- // Reevaluate clipping state since we've relocated and resized the popup
- this.clip();
-
- return this;
-};
-
-/**
- * Set popup alignment
- * @param {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
- * `backwards` or `forwards`.
- */
-OO.ui.PopupWidget.prototype.setAlignment = function ( align ) {
- // Validate alignment and transform deprecated values
- if ( [ 'left', 'right', 'force-left', 'force-right', 'backwards', 'forwards', 'center' ].indexOf( align ) > -1 ) {
- this.align = { left: 'force-right', right: 'force-left' }[ align ] || align;
- } else {
- this.align = 'center';
- }
-};
-
-/**
- * Get popup alignment
- * @return {string} align Alignment of the popup, `center`, `force-left`, `force-right`,
- * `backwards` or `forwards`.
- */
-OO.ui.PopupWidget.prototype.getAlignment = function () {
- return this.align;
-};