summaryrefslogtreecommitdiffstatshomepage
path: root/core/misc/position.es6.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/misc/position.es6.js')
-rw-r--r--core/misc/position.es6.js628
1 files changed, 0 insertions, 628 deletions
diff --git a/core/misc/position.es6.js b/core/misc/position.es6.js
deleted file mode 100644
index a42f2331f24..00000000000
--- a/core/misc/position.es6.js
+++ /dev/null
@@ -1,628 +0,0 @@
-/**
- * @file
- * A modified version of jQuery UI position.
- *
- * Per jQuery UI's public domain license, it is permissible to run modified
- * versions of their code. This file offers the same functionality as what is
- * provided by jQuery UI position, but refactored to meet Drupal coding
- * standards, and restructured so it extends jQuery core instead of jQuery UI.
- *
- * This is provided to support pre-existing code that expects the jQuery
- * position API.
- *
- * @see https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt
- * @see https://raw.githubusercontent.com/jquery/jquery-ui/1.12.1/ui/position.js
- */
-
-/**
- * This provides ported version of jQuery UI position, refactored to not depend
- * on jQuery UI and to meet Drupal JavaScript coding standards. Functionality
- * and usage is identical. It positions an element relative to another. The
- * `position()` function can be called by any jQuery object. Additional details
- * on using `position()` are provided in this file in the docblock for
- * $.fn.position.
- */
-(($) => {
- let cachedScrollbarWidth = null;
- const { max, abs } = Math;
- const regexHorizontal = /left|center|right/;
- const regexVertical = /top|center|bottom/;
- const regexOffset = /[+-]\d+(\.[\d]+)?%?/;
- const regexPosition = /^\w+/;
- const regexPercent = /%$/;
- const _position = $.fn.position;
-
- function getOffsets(offsets, width, height) {
- return [
- parseFloat(offsets[0]) *
- (regexPercent.test(offsets[0]) ? width / 100 : 1),
- parseFloat(offsets[1]) *
- (regexPercent.test(offsets[1]) ? height / 100 : 1),
- ];
- }
-
- function parseCss(element, property) {
- return parseInt($.css(element, property), 10) || 0;
- }
-
- function getDimensions(elem) {
- const raw = elem[0];
- if (raw.nodeType === 9) {
- return {
- width: elem.width(),
- height: elem.height(),
- offset: { top: 0, left: 0 },
- };
- }
- if ($.isWindow(raw)) {
- return {
- width: elem.width(),
- height: elem.height(),
- offset: { top: elem.scrollTop(), left: elem.scrollLeft() },
- };
- }
- if (raw.preventDefault) {
- return {
- width: 0,
- height: 0,
- offset: { top: raw.pageY, left: raw.pageX },
- };
- }
- return {
- width: elem.outerWidth(),
- height: elem.outerHeight(),
- offset: elem.offset(),
- };
- }
-
- const collisions = {
- fit: {
- left(position, data) {
- const { within } = data;
- const withinOffset = within.isWindow
- ? within.scrollLeft
- : within.offset.left;
- const outerWidth = within.width;
- const collisionPosLeft =
- position.left - data.collisionPosition.marginLeft;
- const overLeft = withinOffset - collisionPosLeft;
- const overRight =
- collisionPosLeft + data.collisionWidth - outerWidth - withinOffset;
- let newOverRight;
-
- // Element is wider than within
- if (data.collisionWidth > outerWidth) {
- // Element is initially over the left side of within
- if (overLeft > 0 && overRight <= 0) {
- newOverRight =
- position.left +
- overLeft +
- data.collisionWidth -
- outerWidth -
- withinOffset;
- position.left += overLeft - newOverRight;
-
- // Element is initially over right side of within
- } else if (overRight > 0 && overLeft <= 0) {
- position.left = withinOffset;
-
- // Element is initially over both left and right sides of within
- } else if (overLeft > overRight) {
- position.left = withinOffset + outerWidth - data.collisionWidth;
- } else {
- position.left = withinOffset;
- }
-
- // Too far left -> align with left edge
- } else if (overLeft > 0) {
- position.left += overLeft;
-
- // Too far right -> align with right edge
- } else if (overRight > 0) {
- position.left -= overRight;
-
- // Adjust based on position and margin
- } else {
- position.left = max(position.left - collisionPosLeft, position.left);
- }
- },
- top(position, data) {
- const { within } = data;
- const withinOffset = within.isWindow
- ? within.scrollTop
- : within.offset.top;
- const outerHeight = data.within.height;
- const collisionPosTop = position.top - data.collisionPosition.marginTop;
- const overTop = withinOffset - collisionPosTop;
- const overBottom =
- collisionPosTop + data.collisionHeight - outerHeight - withinOffset;
- let newOverBottom;
-
- // Element is taller than within
- if (data.collisionHeight > outerHeight) {
- // Element is initially over the top of within
- if (overTop > 0 && overBottom <= 0) {
- newOverBottom =
- position.top +
- overTop +
- data.collisionHeight -
- outerHeight -
- withinOffset;
- position.top += overTop - newOverBottom;
-
- // Element is initially over bottom of within
- } else if (overBottom > 0 && overTop <= 0) {
- position.top = withinOffset;
-
- // Element is initially over both top and bottom of within
- } else if (overTop > overBottom) {
- position.top = withinOffset + outerHeight - data.collisionHeight;
- } else {
- position.top = withinOffset;
- }
-
- // Too far up -> align with top
- } else if (overTop > 0) {
- position.top += overTop;
-
- // Too far down -> align with bottom edge
- } else if (overBottom > 0) {
- position.top -= overBottom;
-
- // Adjust based on position and margin
- } else {
- position.top = max(position.top - collisionPosTop, position.top);
- }
- },
- },
- flip: {
- left(position, data) {
- const { within } = data;
- const withinOffset = within.offset.left + within.scrollLeft;
- const outerWidth = within.width;
- const offsetLeft = within.isWindow
- ? within.scrollLeft
- : within.offset.left;
- const collisionPosLeft =
- position.left - data.collisionPosition.marginLeft;
- const overLeft = collisionPosLeft - offsetLeft;
- const overRight =
- collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft;
- const myOffset =
- // eslint-disable-next-line no-nested-ternary
- data.my[0] === 'left'
- ? -data.elemWidth
- : data.my[0] === 'right'
- ? data.elemWidth
- : 0;
- const atOffset =
- // eslint-disable-next-line no-nested-ternary
- data.at[0] === 'left'
- ? data.targetWidth
- : data.at[0] === 'right'
- ? -data.targetWidth
- : 0;
- const offset = -2 * data.offset[0];
- let newOverRight;
- let newOverLeft;
-
- if (overLeft < 0) {
- newOverRight =
- position.left +
- myOffset +
- atOffset +
- offset +
- data.collisionWidth -
- outerWidth -
- withinOffset;
- if (newOverRight < 0 || newOverRight < abs(overLeft)) {
- position.left += myOffset + atOffset + offset;
- }
- } else if (overRight > 0) {
- newOverLeft =
- position.left -
- data.collisionPosition.marginLeft +
- myOffset +
- atOffset +
- offset -
- offsetLeft;
- if (newOverLeft > 0 || abs(newOverLeft) < overRight) {
- position.left += myOffset + atOffset + offset;
- }
- }
- },
- top(position, data) {
- const { within } = data;
- const withinOffset = within.offset.top + within.scrollTop;
- const outerHeight = within.height;
- const offsetTop = within.isWindow
- ? within.scrollTop
- : within.offset.top;
- const collisionPosTop = position.top - data.collisionPosition.marginTop;
- const overTop = collisionPosTop - offsetTop;
- const overBottom =
- collisionPosTop + data.collisionHeight - outerHeight - offsetTop;
- const top = data.my[1] === 'top';
- // eslint-disable-next-line no-nested-ternary
- const myOffset = top
- ? -data.elemHeight
- : data.my[1] === 'bottom'
- ? data.elemHeight
- : 0;
- const atOffset =
- // eslint-disable-next-line no-nested-ternary
- data.at[1] === 'top'
- ? data.targetHeight
- : data.at[1] === 'bottom'
- ? -data.targetHeight
- : 0;
- const offset = -2 * data.offset[1];
- let newOverTop;
- let newOverBottom;
- if (overTop < 0) {
- newOverBottom =
- position.top +
- myOffset +
- atOffset +
- offset +
- data.collisionHeight -
- outerHeight -
- withinOffset;
- if (newOverBottom < 0 || newOverBottom < abs(overTop)) {
- position.top += myOffset + atOffset + offset;
- }
- } else if (overBottom > 0) {
- newOverTop =
- position.top -
- data.collisionPosition.marginTop +
- myOffset +
- atOffset +
- offset -
- offsetTop;
- if (newOverTop > 0 || abs(newOverTop) < overBottom) {
- position.top += myOffset + atOffset + offset;
- }
- }
- },
- },
- flipfit: {
- left(...args) {
- collisions.flip.left.apply(this, args);
- collisions.fit.left.apply(this, args);
- },
- top(...args) {
- collisions.flip.top.apply(this, args);
- collisions.fit.top.apply(this, args);
- },
- },
- };
-
- $.position = {
- scrollbarWidth() {
- if (cachedScrollbarWidth !== undefined) {
- return cachedScrollbarWidth;
- }
- const div = $(
- '<div ' +
- "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
- "<div style='height:100px;width:auto;'></div></div>",
- );
- const innerDiv = div.children()[0];
-
- $('body').append(div);
- const w1 = innerDiv.offsetWidth;
- div.css('overflow', 'scroll');
-
- let w2 = innerDiv.offsetWidth;
-
- if (w1 === w2) {
- w2 = div[0].clientWidth;
- }
-
- div.remove();
- cachedScrollbarWidth = w1 - w2;
- return cachedScrollbarWidth;
- },
- getScrollInfo(within) {
- const overflowX =
- within.isWindow || within.isDocument
- ? ''
- : within.element.css('overflow-x');
- const overflowY =
- within.isWindow || within.isDocument
- ? ''
- : within.element.css('overflow-y');
- const hasOverflowX =
- overflowX === 'scroll' ||
- (overflowX === 'auto' && within.width < within.element[0].scrollWidth);
- const hasOverflowY =
- overflowY === 'scroll' ||
- (overflowY === 'auto' &&
- within.height < within.element[0].scrollHeight);
- return {
- width: hasOverflowY ? $.position.scrollbarWidth() : 0,
- height: hasOverflowX ? $.position.scrollbarWidth() : 0,
- };
- },
- getWithinInfo(element) {
- const withinElement = $(element || window);
- const isWindow = $.isWindow(withinElement[0]);
- const isDocument = !!withinElement[0] && withinElement[0].nodeType === 9;
- const hasOffset = !isWindow && !isDocument;
- return {
- element: withinElement,
- isWindow,
- isDocument,
- offset: hasOffset ? $(element).offset() : { left: 0, top: 0 },
- scrollLeft: withinElement.scrollLeft(),
- scrollTop: withinElement.scrollTop(),
- width: withinElement.outerWidth(),
- height: withinElement.outerHeight(),
- };
- },
- };
-
- // eslint-disable-next-line func-names
- /**
- * Positions an element relative to another.
- *
- * The following documentation is originally from
- * {@link https://api.jqueryui.com/position/}.
- *
- * @param {Object} options - the options object.
- * @param {string} options.my - Defines which position on the element being
- * positioned to align with the target element: "horizontal vertical"
- * alignment. A single value such as "right" will be normalized to "right
- * center", "top" will be normalized to "center top" (following CSS
- * convention). Acceptable horizontal values: "left", "center", "right".
- * Acceptable vertical values: "top", "center", "bottom". Example: "left
- * top" or "center center". Each dimension can also contain offsets, in
- * pixels or percent, e.g., "right+10 top-25%". Percentage offsets are
- * relative to the element being positioned. Default value is "center".
- * @param {string} options.at - Defines which position on the target element
- * to align the positioned element against: "horizontal vertical" alignment.
- * See the `my` option for full details on possible values. Percentage
- * offsets are relative to the target element. Default value is "center".
- * @param {string|Element|jQuery|Event|null} options.of - Which element to
- * position against. If you provide a selector or jQuery object, the first
- * matching element will be used. If you provide an event object, the pageX
- * and pageY properties will be used. Example: "#top-menu". Default value is
- * null.
- * @param {string} options.collision - When the positioned element overflows
- * the window in some direction, move it to an alternative position. Similar
- * to `my` and `at`, this accepts a single value or a pair for
- * horizontal/vertical, e.g., "flip", "fit", "fit flip", "fit none". Default
- * value is "flip". The options work as follows:
- * - "flip": Flips the element to the opposite side of the target and the
- * collision detection is run again to see if it will fit. Whichever side
- * allows more of the element to be visible will be used.
- * - "fit": Shift the element away from the edge of the window.
- * - "flipfit": First applies the flip logic, placing the element on
- * whichever side allows more of the element to be visible. Then the fit
- * logic is applied to ensure as much of the element is visible as
- * possible.
- * "none": Does not apply any collision detection.
- * @param {function|null} options.using - When specified, the actual property
- * setting is delegated to this callback. Receives two parameters: The first
- * is a hash of top and left values for the position that should be set and
- * can be forwarded to .css() or .animate().The second provides feedback
- * about the position and dimensions of both elements, as well as
- * calculations to their relative position. Both target and element have
- * these properties: element, left, top, width, height. In addition, there's
- * horizontal, vertical and important, providing twelve potential directions
- * like { horizontal: "center", vertical: "left", important: "horizontal" }.
- * Default value is null.
- * @param {string|Element|jQuery} options.within - Element to position within,
- * affecting collision detection. If you provide a selector or jQuery
- * object, the first matching element will be used. Default value is window.
- *
- * @return {jQuery}
- * The jQuery object that called called this function.
- */
- $.fn.position = function (options) {
- if (!options || !options.of) {
- // eslint-disable-next-line prefer-rest-params
- return _position.apply(this, arguments);
- }
-
- // Make a copy, we don't want to modify arguments
- options = $.extend({}, options);
-
- const within = $.position.getWithinInfo(options.within);
- const scrollInfo = $.position.getScrollInfo(within);
- const collision = (options.collision || 'flip').split(' ');
- const offsets = {};
-
- // Make sure string options are treated as CSS selectors
- const target =
- typeof options.of === 'string'
- ? $(document).find(options.of)
- : $(options.of);
- const dimensions = getDimensions(target);
- const targetWidth = dimensions.width;
- const targetHeight = dimensions.height;
- const targetOffset = dimensions.offset;
-
- if (target[0].preventDefault) {
- // Force left top to allow flipping
- options.at = 'left top';
- }
-
- // Clone to reuse original targetOffset later
- const basePosition = $.extend({}, targetOffset);
-
- // Force my and at to have valid horizontal and vertical positions
- // if a value is missing or invalid, it will be converted to center
- // eslint-disable-next-line func-names
- $.each(['my', 'at'], function () {
- let pos = (options[this] || '').split(' ');
-
- if (pos.length === 1) {
- // eslint-disable-next-line no-nested-ternary
- pos = regexHorizontal.test(pos[0])
- ? pos.concat(['center'])
- : regexVertical.test(pos[0])
- ? ['center'].concat(pos)
- : ['center', 'center'];
- }
- pos[0] = regexHorizontal.test(pos[0]) ? pos[0] : 'center';
- pos[1] = regexVertical.test(pos[1]) ? pos[1] : 'center';
-
- // Calculate offsets
- const horizontalOffset = regexOffset.exec(pos[0]);
- const verticalOffset = regexOffset.exec(pos[1]);
- offsets[this] = [
- horizontalOffset ? horizontalOffset[0] : 0,
- verticalOffset ? verticalOffset[0] : 0,
- ];
-
- // Reduce to just the positions without the offsets
- options[this] = [
- regexPosition.exec(pos[0])[0],
- regexPosition.exec(pos[1])[0],
- ];
- });
-
- // Normalize collision option
- if (collision.length === 1) {
- // eslint-disable-next-line prefer-destructuring
- collision[1] = collision[0];
- }
-
- if (options.at[0] === 'right') {
- basePosition.left += targetWidth;
- } else if (options.at[0] === 'center') {
- basePosition.left += targetWidth / 2;
- }
-
- if (options.at[1] === 'bottom') {
- basePosition.top += targetHeight;
- } else if (options.at[1] === 'center') {
- basePosition.top += targetHeight / 2;
- }
-
- const atOffset = getOffsets(offsets.at, targetWidth, targetHeight);
- basePosition.left += atOffset[0];
- basePosition.top += atOffset[1];
-
- // eslint-disable-next-line func-names
- return this.each(function () {
- let using;
- const elem = $(this);
- const elemWidth = elem.outerWidth();
- const elemHeight = elem.outerHeight();
- const marginLeft = parseCss(this, 'marginLeft');
- const marginTop = parseCss(this, 'marginTop');
- const collisionWidth =
- elemWidth +
- marginLeft +
- parseCss(this, 'marginRight') +
- scrollInfo.width;
- const collisionHeight =
- elemHeight +
- marginTop +
- parseCss(this, 'marginBottom') +
- scrollInfo.height;
- const position = $.extend({}, basePosition);
- const myOffset = getOffsets(
- offsets.my,
- elem.outerWidth(),
- elem.outerHeight(),
- );
-
- if (options.my[0] === 'right') {
- position.left -= elemWidth;
- } else if (options.my[0] === 'center') {
- position.left -= elemWidth / 2;
- }
-
- if (options.my[1] === 'bottom') {
- position.top -= elemHeight;
- } else if (options.my[1] === 'center') {
- position.top -= elemHeight / 2;
- }
-
- position.left += myOffset[0];
- position.top += myOffset[1];
-
- const collisionPosition = {
- marginLeft,
- marginTop,
- };
-
- // eslint-disable-next-line func-names
- $.each(['left', 'top'], function (i, dir) {
- if (collisions[collision[i]]) {
- collisions[collision[i]][dir](position, {
- targetWidth,
- targetHeight,
- elemWidth,
- elemHeight,
- collisionPosition,
- collisionWidth,
- collisionHeight,
- offset: [atOffset[0] + myOffset[0], atOffset[1] + myOffset[1]],
- my: options.my,
- at: options.at,
- within,
- elem,
- });
- }
- });
-
- if (options.using) {
- // Adds feedback as second argument to using callback, if present
- // eslint-disable-next-line func-names
- using = function (props) {
- const left = targetOffset.left - position.left;
- const right = left + targetWidth - elemWidth;
- const top = targetOffset.top - position.top;
- const bottom = top + targetHeight - elemHeight;
- const feedback = {
- target: {
- element: target,
- left: targetOffset.left,
- top: targetOffset.top,
- width: targetWidth,
- height: targetHeight,
- },
- element: {
- element: elem,
- left: position.left,
- top: position.top,
- width: elemWidth,
- height: elemHeight,
- },
- // eslint-disable-next-line no-nested-ternary
- horizontal: right < 0 ? 'left' : left > 0 ? 'right' : 'center',
- // eslint-disable-next-line no-nested-ternary
- vertical: bottom < 0 ? 'top' : top > 0 ? 'bottom' : 'middle',
- };
- if (targetWidth < elemWidth && abs(left + right) < targetWidth) {
- feedback.horizontal = 'center';
- }
- if (targetHeight < elemHeight && abs(top + bottom) < targetHeight) {
- feedback.vertical = 'middle';
- }
- if (max(abs(left), abs(right)) > max(abs(top), abs(bottom))) {
- feedback.important = 'horizontal';
- } else {
- feedback.important = 'vertical';
- }
- options.using.call(this, props, feedback);
- };
- }
-
- elem.offset($.extend(position, { using }));
- });
- };
-
- // Although $.ui.position is not built to be called directly, some legacy code
- // may have checks for the presence of $.ui.position, which can be used to
- // confirm the presence of jQuery UI position's API, as opposed to the more
- // limited version provided by jQuery.
- if (!$.hasOwnProperty('ui')) {
- $.ui = {};
- }
- $.ui.position = collisions;
-})(jQuery);