User:Chlod/Scripts/InlinePopupWidget.js

/** * PopupWidget with support for wrapped inline elements. * * @class * @extends OO.ui.PopupWidget * @mixins OO.ui.mixin.IconElement * @mixins OO.ui.mixin.LabelElement * @mixins OO.ui.mixin.ClippableElement * @mixins OO.ui.mixin.FloatableElement * * @constructor * @param {Object} [config] Configuration options * @author Chlod Alejandro  * @license MIT */ OO.ui.InlinePopupWidget = function OoUiInlinePopupWidget( config ) { OO.ui.InlinePopupWidget.super.call( this, config ); };

OO.inheritClass( OO.ui.InlinePopupWidget, OO.ui.PopupWidget );

/** * Get the container wrapping information. This checks if the floatable container's start * has a different height from the floatable container's end. Assuming this is an inline * element, this means that the floatable container is wrapping. * * @private * @param {string} [direction] Direction of the floatableContainer. This should be either * 'rtl' or 'ltr'. * @param {Object} [containerPos] Position of the floatableContainer. This should have * 'top', 'bottom', 'left', and 'right' keys. * @return {Object} Object containining wrapping information. */ OO.ui.InlinePopupWidget.prototype.getContainerWrapInfo = function ( direction, containerPos ) { if ( this.$floatableContainer.css( 'display' ) !== 'inline' ) { return { isWrapping: false };	}

var $wrapProbe = $( ' ' ); // Add in the probe this.$floatableContainer.prepend( $wrapProbe ); var startOffset = $wrapProbe.offset, startHeight = $wrapProbe.height; // Reuse the same probe this.$floatableContainer.append( $wrapProbe ); var endOffset = $wrapProbe.offset, endHeight = $wrapProbe.height; // Remove the probe $wrapProbe.remove;

// startOffset and endOffset now has the position of the start and // end of the floatableContainer. Illustration: //	//                     viewport (ltr) //  ++	//   |                                                |	//   |             Lorem ipsum [A] dolor sit amet [B] | //  | [C] consectetur [D] adipiscing elit ... |	//  |                                                |	//   ++	// A = startOffset.left, B = containerPos.right, // C = containerPos.left, D = endOffset.left

if ( startOffset.top !== endOffset.top ) { var containerPosRight = containerPos.left + this.$floatableContainer.width; return { isWrapping: true, startOffset: { top: startOffset.top, bottom: startOffset.top + startHeight, start: direction === 'rtl' ? containerPos.left : startOffset.left, end: direction === 'rtl' ? startOffset.left : containerPosRight },			endOffset: { top: endOffset.top, bottom: endOffset.top + endHeight, start: direction === 'rtl' ? containerPosRight : endOffset.left, end: direction === 'rtl' ? endOffset.left : containerPos.left }		};	} else { return { isWrapping: false };	} };

/** * @inheritdoc */ OO.ui.InlinePopupWidget.prototype.computePosition = function { var $offsetParent = this.$floatable.offsetParent;

if ( $offsetParent.is( 'html' ) ) { // The innerHeight/Width and clientHeight/Width calculations don't work well on the // element, but they do work on the $offsetParent = $( $offsetParent[ 0 ].ownerDocument.body ); }	var isBody = $offsetParent.is( 'body' ); // Find out wrapping information var containerPos = isBody ? this.$floatableContainer.offset : OO.ui.Element.static.getRelativePosition( this.$floatableContainer, $offsetParent ); var wrapInfo = this.getContainerWrapInfo(		this.$floatableContainer.css( 'direction' ),		containerPos	);

// Store the original floatable container (will be faked later) var $originalFloatableContainer = this.$floatableContainer;

if ( wrapInfo.isWrapping ) { // Create a temporary "anchor" element that will be used to position the popup var $anchor = $( ' ' ); $anchor.attr( 'class', 'oo-ui-InlinePopupWidget-anchor' ); $offsetParent.prepend( $anchor ); // true if the anchor is being formed at the start (from the first word to the		// end of the first line), false if the anchor is being formed at the end (from		// the start of the last line to the last word) const usingStart = this.popupPosition === 'above' || this.popupPosition === 'before'; const width = usingStart ? Math.abs( wrapInfo.startOffset.end - wrapInfo.startOffset.start ) : Math.abs( wrapInfo.endOffset.end - wrapInfo.endOffset.start ); $anchor.css( {			display: 'inline-block',			position: 'absolute',			top: usingStart ?				wrapInfo.startOffset.top : wrapInfo.endOffset.top,			left: usingStart ?				Math.min( wrapInfo.startOffset.start, wrapInfo.startOffset.end ) :				Math.min( wrapInfo.endOffset.start, wrapInfo.endOffset.end ),			height: usingStart ?				wrapInfo.startOffset.bottom - wrapInfo.startOffset.top :				wrapInfo.endOffset.bottom - wrapInfo.endOffset.top,			width: width		} ); // Fake the $floatableContainer for the rest of the function this.$floatableContainer = $anchor; }

// Call the main position computation function var computedPosition = OO.ui.PopupWidget.prototype.computePosition.call( this ); // Restore the original $floatableContainer this.$floatableContainer = $originalFloatableContainer; return computedPosition; };