diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.d.ts
new file mode 100644
index 00000000..ee6761c6
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.d.ts
@@ -0,0 +1,11 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isnode
+ */
+/**
+ * Checks if the object is a native DOM Node.
+ */
+export default function isNode(obj: any): obj is Node;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js
new file mode 100644
index 00000000..bf95d715
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js
@@ -0,0 +1,21 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isnode
+ */
+/**
+ * Checks if the object is a native DOM Node.
+ */
+export default function isNode(obj) {
+ if (obj) {
+ if (obj.defaultView) {
+ return obj instanceof obj.defaultView.Document;
+ }
+ else if (obj.ownerDocument && obj.ownerDocument.defaultView) {
+ return obj instanceof obj.ownerDocument.defaultView.Node;
+ }
+ }
+ return false;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.d.ts
new file mode 100644
index 00000000..d4d664fd
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.d.ts
@@ -0,0 +1,11 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isrange
+ */
+/**
+ * Checks if the object is a native DOM Range.
+ */
+export default function isRange(obj: unknown): obj is Range;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js
new file mode 100644
index 00000000..59dca442
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js
@@ -0,0 +1,13 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isrange
+ */
+/**
+ * Checks if the object is a native DOM Range.
+ */
+export default function isRange(obj) {
+ return Object.prototype.toString.apply(obj) == '[object Range]';
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.d.ts
new file mode 100644
index 00000000..b05bd413
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.d.ts
@@ -0,0 +1,11 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/istext
+ */
+/**
+ * Checks if the object is a native DOM Text node.
+ */
+export default function isText(obj: unknown): obj is Text;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js
new file mode 100644
index 00000000..a528e6ad
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js
@@ -0,0 +1,13 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/istext
+ */
+/**
+ * Checks if the object is a native DOM Text node.
+ */
+export default function isText(obj) {
+ return Object.prototype.toString.call(obj) == '[object Text]';
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.d.ts
new file mode 100644
index 00000000..64551f00
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.d.ts
@@ -0,0 +1,10 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * Checks if the given attribute name is valid in terms of HTML.
+ *
+ * @param name Attribute name.
+ */
+export default function isValidAttributeName(name: string): boolean;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js
new file mode 100644
index 00000000..19f4e67d
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js
@@ -0,0 +1,22 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isvalidattributename
+ */
+import global from './global.js';
+/**
+ * Checks if the given attribute name is valid in terms of HTML.
+ *
+ * @param name Attribute name.
+ */
+export default function isValidAttributeName(name) {
+ try {
+ global.document.createAttribute(name);
+ }
+ catch (error) {
+ return false;
+ }
+ return true;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.d.ts
new file mode 100644
index 00000000..a387536e
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.d.ts
@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isvisible
+ */
+/**
+ * Checks whether the element is visible to the user in DOM:
+ *
+ * * connected to the root of the document,
+ * * has no `display: none`,
+ * * has no ancestors with `display: none`.
+ *
+ * **Note**: This helper does not check whether the element is hidden by cropping, overflow, etc..
+ * To check that, use {@link module:utils/dom/rect~Rect} instead.
+ */
+export default function isVisible(element: HTMLElement | null | undefined): boolean;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js
new file mode 100644
index 00000000..08c80b6c
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js
@@ -0,0 +1,20 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/isvisible
+ */
+/**
+ * Checks whether the element is visible to the user in DOM:
+ *
+ * * connected to the root of the document,
+ * * has no `display: none`,
+ * * has no ancestors with `display: none`.
+ *
+ * **Note**: This helper does not check whether the element is hidden by cropping, overflow, etc..
+ * To check that, use {@link module:utils/dom/rect~Rect} instead.
+ */
+export default function isVisible(element) {
+ return !!(element && element.getClientRects && element.getClientRects().length);
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.d.ts
new file mode 100644
index 00000000..16a56eac
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.d.ts
@@ -0,0 +1,11 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/iswindow
+ */
+/**
+ * Checks if the object is a native DOM Window.
+ */
+export default function isWindow(obj: unknown): obj is Window;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js
new file mode 100644
index 00000000..ddaa2472
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js
@@ -0,0 +1,22 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/iswindow
+ */
+/**
+ * Checks if the object is a native DOM Window.
+ */
+export default function isWindow(obj) {
+ const stringifiedObject = Object.prototype.toString.apply(obj);
+ // Returns `true` for the `window` object in browser environments.
+ if (stringifiedObject == '[object Window]') {
+ return true;
+ }
+ // Returns `true` for the `window` object in the Electron environment.
+ if (stringifiedObject == '[object global]') {
+ return true;
+ }
+ return false;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.d.ts
new file mode 100644
index 00000000..a1d8ca40
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.d.ts
@@ -0,0 +1,211 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+import Rect, { type RectSource } from '@ckeditor/ckeditor5-utils/src/dom/rect.js';
+/**
+ * Calculates the `position: absolute` coordinates of a given element so it can be positioned with respect to the
+ * target in the visually most efficient way, taking various restrictions like viewport or limiter geometry
+ * into consideration.
+ *
+ * **Note**: If there are no position coordinates found that meet the requirements (arguments of this helper),
+ * `null` is returned.
+ *
+ * ```ts
+ * // The element which is to be positioned.
+ * const element = document.body.querySelector( '#toolbar' );
+ *
+ * // A target to which the element is positioned relatively.
+ * const target = document.body.querySelector( '#container' );
+ *
+ * // Finding the optimal coordinates for the positioning.
+ * const { left, top, name } = getOptimalPosition( {
+ * element: element,
+ * target: target,
+ *
+ * // The algorithm will chose among these positions to meet the requirements such
+ * // as "limiter" element or "fitInViewport", set below. The positions are considered
+ * // in the order of the array.
+ * positions: [
+ * //
+ * // [ Target ]
+ * // +-----------------+
+ * // | Element |
+ * // +-----------------+
+ * //
+ * targetRect => ( {
+ * top: targetRect.bottom,
+ * left: targetRect.left,
+ * name: 'mySouthEastPosition'
+ * } ),
+ *
+ * //
+ * // +-----------------+
+ * // | Element |
+ * // +-----------------+
+ * // [ Target ]
+ * //
+ * ( targetRect, elementRect ) => ( {
+ * top: targetRect.top - elementRect.height,
+ * left: targetRect.left,
+ * name: 'myNorthEastPosition'
+ * } )
+ * ],
+ *
+ * // Find a position such guarantees the element remains within visible boundaries of
.
+ * limiter: document.body,
+ *
+ * // Find a position such guarantees the element remains within visible boundaries of the browser viewport.
+ * fitInViewport: true
+ * } );
+ *
+ * // The best position which fits into document.body and the viewport. May be useful
+ * // to set proper class on the `element`.
+ * console.log( name ); // -> "myNorthEastPosition"
+ *
+ * // Using the absolute coordinates which has been found to position the element
+ * // as in the diagram depicting the "myNorthEastPosition" position.
+ * element.style.top = top;
+ * element.style.left = left;
+ * ```
+ *
+ * @param options The input data and configuration of the helper.
+ */
+export declare function getOptimalPosition({ element, target, positions, limiter, fitInViewport, viewportOffsetConfig }: Options): DomPoint | null;
+/**
+ * A position object which instances are created and used by the {@link module:utils/dom/position~getOptimalPosition} helper.
+ *
+ * {@link module:utils/dom/position~DomPoint#top} and {@link module:utils/dom/position~DomPoint#left} properties of the position instance
+ * translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
+ * in DOM, they will make it display it in the right place in the viewport.
+ */
+export interface DomPoint {
+ /**
+ * Position name.
+ */
+ readonly name?: string;
+ /**
+ * Additional position configuration, as passed from the {@link module:utils/dom/position~PositioningFunction positioning function}.
+ *
+ * This object can be use, for instance, to pass through presentation options used by the consumer of the
+ * {@link module:utils/dom/position~getOptimalPosition} helper.
+ */
+ readonly config?: object;
+ /**
+ * The left value in pixels in the CSS `position: absolute` coordinate system.
+ * Set it on the positioned element in DOM to move it to the position.
+ */
+ readonly left: number;
+ /**
+ * The top value in pixels in the CSS `position: absolute` coordinate system.
+ * Set it on the positioned element in DOM to move it to the position.
+ */
+ readonly top: number;
+}
+/**
+ * The `getOptimalPosition()` helper options.
+ */
+export interface Options {
+ /**
+ * Element that is to be positioned.
+ */
+ readonly element: HTMLElement;
+ /**
+ * Target with respect to which the `element` is to be positioned.
+ */
+ readonly target: RectSource | (() => RectSource);
+ /**
+ * An array of positioning functions.
+ *
+ * **Note**: Positioning functions are processed in the order of preference. The first function that works
+ * in the current environment (e.g. offers the complete fit in the viewport geometry) will be picked by
+ * `getOptimalPosition()`.
+ *
+ * **Note**: Any positioning function returning `null` is ignored.
+ */
+ readonly positions: ReadonlyArray;
+ /**
+ * When set, the algorithm will chose position which fits the most in the
+ * limiter's bounding rect.
+ */
+ readonly limiter?: RectSource | (() => (RectSource | null)) | null;
+ /**
+ * When set, the algorithm will chose such a position which fits `element`
+ * the most inside visible viewport.
+ */
+ readonly fitInViewport?: boolean;
+ /**
+ * Viewport offset config object. It restricts the visible viewport available to the `getOptimalPosition()` from each side.
+ *
+ * ```ts
+ * {
+ * top: 50,
+ * right: 50,
+ * bottom: 50,
+ * left: 50
+ * }
+ * ```
+ */
+ readonly viewportOffsetConfig?: {
+ readonly top?: number;
+ readonly right?: number;
+ readonly bottom?: number;
+ readonly left?: number;
+ };
+}
+/**
+ * A positioning function which, based on positioned element and target {@link module:utils/dom/rect~Rect Rects}, returns rect coordinates
+ * representing the geometrical relation between them. Used by the {@link module:utils/dom/position~getOptimalPosition} helper.
+ *
+ * ```ts
+ * // This simple position will place the element directly under the target, in the middle:
+ * //
+ * // [ Target ]
+ * // +-----------------+
+ * // | Element |
+ * // +-----------------+
+ * //
+ * const position = ( targetRect, elementRect, [ viewportRect ] ) => ( {
+ * top: targetRect.bottom,
+ * left: targetRect.left + targetRect.width / 2 - elementRect.width / 2,
+ * name: 'bottomMiddle',
+ *
+ * // Note: The config is optional.
+ * config: {
+ * zIndex: '999'
+ * }
+ * } );
+ * ```
+ *
+ * @param elementRect The rect of the element to be positioned.
+ * @param targetRect The rect of the target the element (its rect) is relatively positioned to.
+ * @param viewportRect The rect of the visual browser viewport.
+ * @returns When the function returns `null`, it will not be considered by {@link module:utils/dom/position~getOptimalPosition}.
+ */
+export type PositioningFunction = (elementRect: Rect, targetRect: Rect, viewportRect: Rect, limiterRect?: Rect) => PositioningFunctionResult | null;
+/**
+ * The result of {@link module:utils/dom/position~PositioningFunction}.
+ */
+export interface PositioningFunctionResult {
+ /**
+ * The `top` value of the element rect that would represent the position.
+ */
+ top: number;
+ /**
+ * The `left` value of the element rect that would represent the position.
+ */
+ left: number;
+ /**
+ * The name of the position. It helps the user of the {@link module:utils/dom/position~getOptimalPosition}
+ * helper to recognize different positioning function results. It will pass through to the {@link module:utils/dom/position~DomPoint}
+ * returned by the helper.
+ */
+ name?: string;
+ /**
+ * An optional configuration that will pass-through the {@link module:utils/dom/position~getOptimalPosition} helper
+ * to the {@link module:utils/dom/position~DomPoint} returned by this helper.
+ * This configuration may, for instance, let the user of {@link module:utils/dom/position~getOptimalPosition} know that this particular
+ * position comes with a certain presentation.
+ */
+ config?: object;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js
new file mode 100644
index 00000000..3e4c6a14
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js
@@ -0,0 +1,313 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/position
+ */
+import global from './global.js';
+import Rect from './rect.js';
+import getPositionedAncestor from './getpositionedancestor.js';
+import { isFunction } from 'lodash-es';
+// @if CK_DEBUG_POSITION // const {
+// @if CK_DEBUG_POSITION // default: RectDrawer,
+// @if CK_DEBUG_POSITION // diagonalStylesBlack,
+// @if CK_DEBUG_POSITION // diagonalStylesGreen,
+// @if CK_DEBUG_POSITION // diagonalStylesRed
+// @if CK_DEBUG_POSITION // } = require( '@ckeditor/ckeditor5-utils/tests/_utils/rectdrawer' );
+// @if CK_DEBUG_POSITION // const TARGET_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // outlineWidth: '2px', outlineStyle: 'dashed', outlineColor: 'blue', outlineOffset: '2px'
+// @if CK_DEBUG_POSITION // };
+// @if CK_DEBUG_POSITION // const VISIBLE_TARGET_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // ...diagonalStylesBlack,
+// @if CK_DEBUG_POSITION // opacity: '1',
+// @if CK_DEBUG_POSITION // backgroundColor: '#00000033',
+// @if CK_DEBUG_POSITION // outlineWidth: '2px'
+// @if CK_DEBUG_POSITION // };
+// @if CK_DEBUG_POSITION // const VIEWPORT_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // outlineWidth: '2px',
+// @if CK_DEBUG_POSITION // outlineOffset: '-2px',
+// @if CK_DEBUG_POSITION // outlineStyle: 'solid',
+// @if CK_DEBUG_POSITION // outlineColor: 'red'
+// @if CK_DEBUG_POSITION // };
+// @if CK_DEBUG_POSITION // const VISIBLE_LIMITER_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // ...diagonalStylesGreen,
+// @if CK_DEBUG_POSITION // outlineWidth: '2px',
+// @if CK_DEBUG_POSITION // outlineOffset: '-2px'
+// @if CK_DEBUG_POSITION // };
+// @if CK_DEBUG_POSITION // const ELEMENT_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // outlineWidth: '2px', outlineColor: 'orange', outlineOffset: '-2px'
+// @if CK_DEBUG_POSITION // };
+// @if CK_DEBUG_POSITION // const CHOSEN_POSITION_RECT_STYLE = {
+// @if CK_DEBUG_POSITION // opacity: .5, outlineColor: 'magenta', backgroundColor: 'magenta'
+// @if CK_DEBUG_POSITION // };
+/**
+ * Calculates the `position: absolute` coordinates of a given element so it can be positioned with respect to the
+ * target in the visually most efficient way, taking various restrictions like viewport or limiter geometry
+ * into consideration.
+ *
+ * **Note**: If there are no position coordinates found that meet the requirements (arguments of this helper),
+ * `null` is returned.
+ *
+ * ```ts
+ * // The element which is to be positioned.
+ * const element = document.body.querySelector( '#toolbar' );
+ *
+ * // A target to which the element is positioned relatively.
+ * const target = document.body.querySelector( '#container' );
+ *
+ * // Finding the optimal coordinates for the positioning.
+ * const { left, top, name } = getOptimalPosition( {
+ * element: element,
+ * target: target,
+ *
+ * // The algorithm will chose among these positions to meet the requirements such
+ * // as "limiter" element or "fitInViewport", set below. The positions are considered
+ * // in the order of the array.
+ * positions: [
+ * //
+ * // [ Target ]
+ * // +-----------------+
+ * // | Element |
+ * // +-----------------+
+ * //
+ * targetRect => ( {
+ * top: targetRect.bottom,
+ * left: targetRect.left,
+ * name: 'mySouthEastPosition'
+ * } ),
+ *
+ * //
+ * // +-----------------+
+ * // | Element |
+ * // +-----------------+
+ * // [ Target ]
+ * //
+ * ( targetRect, elementRect ) => ( {
+ * top: targetRect.top - elementRect.height,
+ * left: targetRect.left,
+ * name: 'myNorthEastPosition'
+ * } )
+ * ],
+ *
+ * // Find a position such guarantees the element remains within visible boundaries of .
+ * limiter: document.body,
+ *
+ * // Find a position such guarantees the element remains within visible boundaries of the browser viewport.
+ * fitInViewport: true
+ * } );
+ *
+ * // The best position which fits into document.body and the viewport. May be useful
+ * // to set proper class on the `element`.
+ * console.log( name ); // -> "myNorthEastPosition"
+ *
+ * // Using the absolute coordinates which has been found to position the element
+ * // as in the diagram depicting the "myNorthEastPosition" position.
+ * element.style.top = top;
+ * element.style.left = left;
+ * ```
+ *
+ * @param options The input data and configuration of the helper.
+ */
+export function getOptimalPosition({ element, target, positions, limiter, fitInViewport, viewportOffsetConfig }) {
+ // If the {@link module:utils/dom/position~Options#target} is a function, use what it returns.
+ // https://github.com/ckeditor/ckeditor5-utils/issues/157
+ if (isFunction(target)) {
+ target = target();
+ }
+ // If the {@link module:utils/dom/position~Options#limiter} is a function, use what it returns.
+ // https://github.com/ckeditor/ckeditor5-ui/issues/260
+ if (isFunction(limiter)) {
+ limiter = limiter();
+ }
+ const positionedElementAncestor = getPositionedAncestor(element);
+ const constrainedViewportRect = getConstrainedViewportRect(viewportOffsetConfig);
+ const elementRect = new Rect(element);
+ const visibleTargetRect = getVisibleViewportIntersectionRect(target, constrainedViewportRect);
+ let bestPosition;
+ // @if CK_DEBUG_POSITION // const targetRect = new Rect( target );
+ // @if CK_DEBUG_POSITION // RectDrawer.clear();
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( targetRect, TARGET_RECT_STYLE, 'Target' );
+ // @if CK_DEBUG_POSITION // if ( constrainedViewportRect ) {
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( constrainedViewportRect, VIEWPORT_RECT_STYLE, 'Viewport' );
+ // @if CK_DEBUG_POSITION // }
+ // If the target got cropped by ancestors or went off the screen, positioning does not make any sense.
+ if (!visibleTargetRect || !constrainedViewportRect.getIntersection(visibleTargetRect)) {
+ return null;
+ }
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( visibleTargetRect, VISIBLE_TARGET_RECT_STYLE, 'VisTgt' );
+ const positionOptions = {
+ targetRect: visibleTargetRect,
+ elementRect,
+ positionedElementAncestor,
+ viewportRect: constrainedViewportRect
+ };
+ // If there are no limits, just grab the very first position and be done with that drama.
+ if (!limiter && !fitInViewport) {
+ bestPosition = new PositionObject(positions[0], positionOptions);
+ }
+ else {
+ if (limiter) {
+ const visibleLimiterRect = getVisibleViewportIntersectionRect(limiter, constrainedViewportRect);
+ if (visibleLimiterRect) {
+ positionOptions.limiterRect = visibleLimiterRect;
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( visibleLimiterRect, VISIBLE_LIMITER_RECT_STYLE, 'VisLim' );
+ }
+ }
+ // If there's no best position found, i.e. when all intersections have no area because
+ // rects have no width or height, then just return `null`
+ bestPosition = getBestPosition(positions, positionOptions);
+ }
+ return bestPosition;
+}
+/**
+ * Returns intersection of visible source `Rect` with Viewport `Rect`. In case when source `Rect` is not visible
+ * or there is no intersection between source `Rect` and Viewport `Rect`, `null` will be returned.
+ */
+function getVisibleViewportIntersectionRect(source, viewportRect) {
+ const visibleSourceRect = new Rect(source).getVisible();
+ if (!visibleSourceRect) {
+ return null;
+ }
+ return visibleSourceRect.getIntersection(viewportRect);
+}
+/**
+ * Returns a viewport `Rect` shrunk by the viewport offset config from all sides.
+ */
+function getConstrainedViewportRect(viewportOffsetConfig) {
+ viewportOffsetConfig = Object.assign({ top: 0, bottom: 0, left: 0, right: 0 }, viewportOffsetConfig);
+ const viewportRect = new Rect(global.window);
+ viewportRect.top += viewportOffsetConfig.top;
+ viewportRect.height -= viewportOffsetConfig.top;
+ viewportRect.bottom -= viewportOffsetConfig.bottom;
+ viewportRect.height -= viewportOffsetConfig.bottom;
+ return viewportRect;
+}
+/**
+ * For a given array of positioning functions, returns such that provides the best
+ * fit of the `elementRect` into the `limiterRect` and `viewportRect`.
+ */
+function getBestPosition(positions, options) {
+ const { elementRect } = options;
+ // This is when element is fully visible.
+ const elementRectArea = elementRect.getArea();
+ const positionInstances = positions
+ .map(positioningFunction => new PositionObject(positioningFunction, options))
+ // Some positioning functions may return `null` if they don't want to participate.
+ .filter(position => !!position.name);
+ let maxFitFactor = 0;
+ let bestPosition = null;
+ for (const position of positionInstances) {
+ const { limiterIntersectionArea, viewportIntersectionArea } = position;
+ // If a such position is found that element is fully contained by the limiter then, obviously,
+ // there will be no better one, so finishing.
+ if (limiterIntersectionArea === elementRectArea) {
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( position._rect, CHOSEN_POSITION_RECT_STYLE, [
+ // @if CK_DEBUG_POSITION // position.name,
+ // @if CK_DEBUG_POSITION // '100% fit',
+ // @if CK_DEBUG_POSITION // ].join( '\n' ) );
+ return position;
+ }
+ // To maximize both viewport and limiter intersection areas we use distance on _viewportIntersectionArea
+ // and _limiterIntersectionArea plane (without sqrt because we are looking for max value).
+ const fitFactor = viewportIntersectionArea ** 2 + limiterIntersectionArea ** 2;
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( position._rect, { opacity: .4 }, [
+ // @if CK_DEBUG_POSITION // position.name,
+ // @if CK_DEBUG_POSITION // 'Vi=' + Math.round( viewportIntersectionArea ),
+ // @if CK_DEBUG_POSITION // 'Li=' + Math.round( limiterIntersectionArea )
+ // @if CK_DEBUG_POSITION // ].join( '\n' ) );
+ if (fitFactor > maxFitFactor) {
+ maxFitFactor = fitFactor;
+ bestPosition = position;
+ }
+ }
+ // @if CK_DEBUG_POSITION // if ( bestPosition ) {
+ // @if CK_DEBUG_POSITION // RectDrawer.draw( bestPosition._rect, CHOSEN_POSITION_RECT_STYLE );
+ // @if CK_DEBUG_POSITION // }
+ return bestPosition;
+}
+/**
+ * A position class which instances are created and used by the {@link module:utils/dom/position~getOptimalPosition} helper.
+ *
+ * {@link module:utils/dom/position~Position#top} and {@link module:utils/dom/position~Position#left} properties of the position instance
+ * translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
+ * in DOM, they will make it display it in the right place in the viewport.
+ */
+class PositionObject {
+ /**
+ * Creates an instance of the {@link module:utils/dom/position~PositionObject} class.
+ *
+ * @param positioningFunction function The function that defines the expected
+ * coordinates the positioned element should move to.
+ * @param options options object.
+ * @param options.elementRect The positioned element rect.
+ * @param options.targetRect The target element rect.
+ * @param options.viewportRect The viewport rect.
+ * @param options.limiterRect The limiter rect.
+ * @param options.positionedElementAncestor Nearest element ancestor element which CSS position is not "static".
+ */
+ constructor(positioningFunction, options) {
+ const positioningFunctionOutput = positioningFunction(options.targetRect, options.elementRect, options.viewportRect, options.limiterRect);
+ // Nameless position for a function that didn't participate.
+ if (!positioningFunctionOutput) {
+ return;
+ }
+ const { left, top, name, config } = positioningFunctionOutput;
+ this.name = name;
+ this.config = config;
+ this._positioningFunctionCoordinates = { left, top };
+ this._options = options;
+ }
+ /**
+ * The left value in pixels in the CSS `position: absolute` coordinate system.
+ * Set it on the positioned element in DOM to move it to the position.
+ */
+ get left() {
+ return this._absoluteRect.left;
+ }
+ /**
+ * The top value in pixels in the CSS `position: absolute` coordinate system.
+ * Set it on the positioned element in DOM to move it to the position.
+ */
+ get top() {
+ return this._absoluteRect.top;
+ }
+ /**
+ * An intersection area between positioned element and limiter within viewport constraints.
+ */
+ get limiterIntersectionArea() {
+ const limiterRect = this._options.limiterRect;
+ if (limiterRect) {
+ return limiterRect.getIntersectionArea(this._rect);
+ }
+ return 0;
+ }
+ /**
+ * An intersection area between positioned element and viewport.
+ */
+ get viewportIntersectionArea() {
+ const viewportRect = this._options.viewportRect;
+ return viewportRect.getIntersectionArea(this._rect);
+ }
+ /**
+ * An already positioned element rect. A clone of the element rect passed to the constructor
+ * but placed in the viewport according to the positioning function.
+ */
+ get _rect() {
+ if (this._cachedRect) {
+ return this._cachedRect;
+ }
+ this._cachedRect = this._options.elementRect.clone().moveTo(this._positioningFunctionCoordinates.left, this._positioningFunctionCoordinates.top);
+ return this._cachedRect;
+ }
+ /**
+ * An already absolutely positioned element rect. See ({@link #_rect}).
+ */
+ get _absoluteRect() {
+ if (this._cachedAbsoluteRect) {
+ return this._cachedAbsoluteRect;
+ }
+ this._cachedAbsoluteRect = this._rect.toAbsoluteRect();
+ return this._cachedAbsoluteRect;
+ }
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.d.ts
new file mode 100644
index 00000000..6ab21a7d
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.d.ts
@@ -0,0 +1,195 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * A helper class representing a `ClientRect` object, e.g. value returned by
+ * the native `object.getBoundingClientRect()` method. Provides a set of methods
+ * to manipulate the rect and compare it against other rect instances.
+ */
+export default class Rect {
+ /**
+ * The "top" value of the rect.
+ *
+ * @readonly
+ */
+ top: number;
+ /**
+ * The "right" value of the rect.
+ *
+ * @readonly
+ */
+ right: number;
+ /**
+ * The "bottom" value of the rect.
+ *
+ * @readonly
+ */
+ bottom: number;
+ /**
+ * The "left" value of the rect.
+ *
+ * @readonly
+ */
+ left: number;
+ /**
+ * The "width" value of the rect.
+ *
+ * @readonly
+ */
+ width: number;
+ /**
+ * The "height" value of the rect.
+ *
+ * @readonly
+ */
+ height: number;
+ /**
+ * The object this rect is for.
+ *
+ * @readonly
+ */
+ private _source;
+ /**
+ * Creates an instance of rect.
+ *
+ * ```ts
+ * // Rect of an HTMLElement.
+ * const rectA = new Rect( document.body );
+ *
+ * // Rect of a DOM Range.
+ * const rectB = new Rect( document.getSelection().getRangeAt( 0 ) );
+ *
+ * // Rect of a window (web browser viewport).
+ * const rectC = new Rect( window );
+ *
+ * // Rect out of an object.
+ * const rectD = new Rect( { top: 0, right: 10, bottom: 10, left: 0, width: 10, height: 10 } );
+ *
+ * // Rect out of another Rect instance.
+ * const rectE = new Rect( rectD );
+ *
+ * // Rect out of a ClientRect.
+ * const rectF = new Rect( document.body.getClientRects().item( 0 ) );
+ * ```
+ *
+ * **Note**: By default a rect of an HTML element includes its CSS borders and scrollbars (if any)
+ * ant the rect of a `window` includes scrollbars too. Use {@link #excludeScrollbarsAndBorders}
+ * to get the inner part of the rect.
+ *
+ * @param source A source object to create the rect.
+ */
+ constructor(source: RectSource);
+ /**
+ * Returns a clone of the rect.
+ *
+ * @returns A cloned rect.
+ */
+ clone(): Rect;
+ /**
+ * Moves the rect so that its upper–left corner lands in desired `[ x, y ]` location.
+ *
+ * @param x Desired horizontal location.
+ * @param y Desired vertical location.
+ * @returns A rect which has been moved.
+ */
+ moveTo(x: number, y: number): this;
+ /**
+ * Moves the rect in–place by a dedicated offset.
+ *
+ * @param x A horizontal offset.
+ * @param y A vertical offset
+ * @returns A rect which has been moved.
+ */
+ moveBy(x: number, y: number): this;
+ /**
+ * Returns a new rect a a result of intersection with another rect.
+ */
+ getIntersection(anotherRect: Rect): Rect | null;
+ /**
+ * Returns the area of intersection with another rect.
+ *
+ * @returns Area of intersection.
+ */
+ getIntersectionArea(anotherRect: Rect): number;
+ /**
+ * Returns the area of the rect.
+ */
+ getArea(): number;
+ /**
+ * Returns a new rect, a part of the original rect, which is actually visible to the user and is relative to the,`body`,
+ * e.g. an original rect cropped by parent element rects which have `overflow` set in CSS
+ * other than `"visible"`.
+ *
+ * If there's no such visible rect, which is when the rect is limited by one or many of
+ * the ancestors, `null` is returned.
+ *
+ * **Note**: This method does not consider the boundaries of the viewport (window).
+ * To get a rect cropped by all ancestors and the viewport, use an intersection such as:
+ *
+ * ```ts
+ * const visibleInViewportRect = new Rect( window ).getIntersection( new Rect( source ).getVisible() );
+ * ```
+ *
+ * @returns A visible rect instance or `null`, if there's none.
+ */
+ getVisible(): Rect | null;
+ /**
+ * Checks if all property values ({@link #top}, {@link #left}, {@link #right},
+ * {@link #bottom}, {@link #width} and {@link #height}) are the equal in both rect
+ * instances.
+ *
+ * @param anotherRect A rect instance to compare with.
+ * @returns `true` when Rects are equal. `false` otherwise.
+ */
+ isEqual(anotherRect: Rect): boolean;
+ /**
+ * Checks whether a rect fully contains another rect instance.
+ *
+ * @param anotherRect
+ * @returns `true` if contains, `false` otherwise.
+ */
+ contains(anotherRect: Rect): boolean;
+ /**
+ * Recalculates screen coordinates to coordinates relative to the positioned ancestor offset.
+ */
+ toAbsoluteRect(): Rect;
+ /**
+ * Excludes scrollbars and CSS borders from the rect.
+ *
+ * * Borders are removed when {@link #_source} is an HTML element.
+ * * Scrollbars are excluded from HTML elements and the `window`.
+ *
+ * @returns A rect which has been updated.
+ */
+ excludeScrollbarsAndBorders(): this;
+ /**
+ * Returns an array of rects of the given native DOM Range.
+ *
+ * @param range A native DOM range.
+ * @returns DOM Range rects.
+ */
+ static getDomRangeRects(range: Range): Array;
+ /**
+ * Returns a bounding rectangle that contains all the given `rects`.
+ *
+ * @param rects A list of rectangles that should be contained in the result rectangle.
+ * @returns Bounding rectangle or `null` if no `rects` were given.
+ */
+ static getBoundingRect(rects: Iterable): Rect | null;
+}
+/**
+ * A source of {@link module:utils/dom/rect~Rect}.
+ */
+export type RectSource = HTMLElement | Range | Window | RectLike;
+/**
+ * An object that describes properties of `ClientRect` object.
+ */
+export interface RectLike {
+ readonly top: number;
+ readonly right: number;
+ readonly bottom: number;
+ readonly left: number;
+ readonly width: number;
+ readonly height: number;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js
new file mode 100644
index 00000000..76a6001c
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js
@@ -0,0 +1,474 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/rect
+ */
+import isRange from './isrange.js';
+import isWindow from './iswindow.js';
+import getBorderWidths from './getborderwidths.js';
+import isText from './istext.js';
+import getPositionedAncestor from './getpositionedancestor.js';
+import global from './global.js';
+const rectProperties = ['top', 'right', 'bottom', 'left', 'width', 'height'];
+/**
+ * A helper class representing a `ClientRect` object, e.g. value returned by
+ * the native `object.getBoundingClientRect()` method. Provides a set of methods
+ * to manipulate the rect and compare it against other rect instances.
+ */
+export default class Rect {
+ /**
+ * Creates an instance of rect.
+ *
+ * ```ts
+ * // Rect of an HTMLElement.
+ * const rectA = new Rect( document.body );
+ *
+ * // Rect of a DOM Range.
+ * const rectB = new Rect( document.getSelection().getRangeAt( 0 ) );
+ *
+ * // Rect of a window (web browser viewport).
+ * const rectC = new Rect( window );
+ *
+ * // Rect out of an object.
+ * const rectD = new Rect( { top: 0, right: 10, bottom: 10, left: 0, width: 10, height: 10 } );
+ *
+ * // Rect out of another Rect instance.
+ * const rectE = new Rect( rectD );
+ *
+ * // Rect out of a ClientRect.
+ * const rectF = new Rect( document.body.getClientRects().item( 0 ) );
+ * ```
+ *
+ * **Note**: By default a rect of an HTML element includes its CSS borders and scrollbars (if any)
+ * ant the rect of a `window` includes scrollbars too. Use {@link #excludeScrollbarsAndBorders}
+ * to get the inner part of the rect.
+ *
+ * @param source A source object to create the rect.
+ */
+ constructor(source) {
+ const isSourceRange = isRange(source);
+ Object.defineProperty(this, '_source', {
+ // If the source is a Rect instance, copy it's #_source.
+ value: source._source || source,
+ writable: true,
+ enumerable: false
+ });
+ if (isDomElement(source) || isSourceRange) {
+ // The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods. If the source
+ // of a rect in an HTML element or a DOM range but it does not belong to any rendered DOM tree, these methods
+ // will fail to obtain the geometry and the rect instance makes little sense to the features using it.
+ // To get rid of this warning make sure the source passed to the constructor is a descendant of `window.document.body`.
+ // @if CK_DEBUG // const sourceNode = isSourceRange ? source.startContainer : source;
+ // @if CK_DEBUG // if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
+ // @if CK_DEBUG // console.warn(
+ // @if CK_DEBUG // 'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
+ // @if CK_DEBUG // { source } );
+ // @if CK_DEBUG // }
+ if (isSourceRange) {
+ const rangeRects = Rect.getDomRangeRects(source);
+ copyRectProperties(this, Rect.getBoundingRect(rangeRects));
+ }
+ else {
+ copyRectProperties(this, source.getBoundingClientRect());
+ }
+ }
+ else if (isWindow(source)) {
+ const { innerWidth, innerHeight } = source;
+ copyRectProperties(this, {
+ top: 0,
+ right: innerWidth,
+ bottom: innerHeight,
+ left: 0,
+ width: innerWidth,
+ height: innerHeight
+ });
+ }
+ else {
+ copyRectProperties(this, source);
+ }
+ }
+ /**
+ * Returns a clone of the rect.
+ *
+ * @returns A cloned rect.
+ */
+ clone() {
+ return new Rect(this);
+ }
+ /**
+ * Moves the rect so that its upper–left corner lands in desired `[ x, y ]` location.
+ *
+ * @param x Desired horizontal location.
+ * @param y Desired vertical location.
+ * @returns A rect which has been moved.
+ */
+ moveTo(x, y) {
+ this.top = y;
+ this.right = x + this.width;
+ this.bottom = y + this.height;
+ this.left = x;
+ return this;
+ }
+ /**
+ * Moves the rect in–place by a dedicated offset.
+ *
+ * @param x A horizontal offset.
+ * @param y A vertical offset
+ * @returns A rect which has been moved.
+ */
+ moveBy(x, y) {
+ this.top += y;
+ this.right += x;
+ this.left += x;
+ this.bottom += y;
+ return this;
+ }
+ /**
+ * Returns a new rect a a result of intersection with another rect.
+ */
+ getIntersection(anotherRect) {
+ const rect = {
+ top: Math.max(this.top, anotherRect.top),
+ right: Math.min(this.right, anotherRect.right),
+ bottom: Math.min(this.bottom, anotherRect.bottom),
+ left: Math.max(this.left, anotherRect.left),
+ width: 0,
+ height: 0
+ };
+ rect.width = rect.right - rect.left;
+ rect.height = rect.bottom - rect.top;
+ if (rect.width < 0 || rect.height < 0) {
+ return null;
+ }
+ else {
+ const newRect = new Rect(rect);
+ newRect._source = this._source;
+ return newRect;
+ }
+ }
+ /**
+ * Returns the area of intersection with another rect.
+ *
+ * @returns Area of intersection.
+ */
+ getIntersectionArea(anotherRect) {
+ const rect = this.getIntersection(anotherRect);
+ if (rect) {
+ return rect.getArea();
+ }
+ else {
+ return 0;
+ }
+ }
+ /**
+ * Returns the area of the rect.
+ */
+ getArea() {
+ return this.width * this.height;
+ }
+ /**
+ * Returns a new rect, a part of the original rect, which is actually visible to the user and is relative to the,`body`,
+ * e.g. an original rect cropped by parent element rects which have `overflow` set in CSS
+ * other than `"visible"`.
+ *
+ * If there's no such visible rect, which is when the rect is limited by one or many of
+ * the ancestors, `null` is returned.
+ *
+ * **Note**: This method does not consider the boundaries of the viewport (window).
+ * To get a rect cropped by all ancestors and the viewport, use an intersection such as:
+ *
+ * ```ts
+ * const visibleInViewportRect = new Rect( window ).getIntersection( new Rect( source ).getVisible() );
+ * ```
+ *
+ * @returns A visible rect instance or `null`, if there's none.
+ */
+ getVisible() {
+ const source = this._source;
+ let visibleRect = this.clone();
+ // There's no ancestor to crop with the overflow.
+ if (isBody(source)) {
+ return visibleRect;
+ }
+ let child = source;
+ let parent = source.parentNode || source.commonAncestorContainer;
+ let absolutelyPositionedChildElement;
+ // Check the ancestors all the way up to the .
+ while (parent && !isBody(parent)) {
+ const isParentOverflowVisible = getElementOverflow(parent) === 'visible';
+ if (child instanceof HTMLElement && getElementPosition(child) === 'absolute') {
+ absolutelyPositionedChildElement = child;
+ }
+ const parentElementPosition = getElementPosition(parent);
+ // The child will be cropped only if it has `position: absolute` and the parent has `position: relative` + some overflow.
+ // Otherwise there's no chance of visual clipping and the parent can be skipped
+ // https://github.com/ckeditor/ckeditor5/issues/14107.
+ //
+ // condition: isParentOverflowVisible
+ // +---------------------------+
+ // | #parent |
+ // | (overflow: visible) |
+ // | +-----------+---------------+
+ // | | child |
+ // | +-----------+---------------+
+ // +---------------------------+
+ //
+ // condition: absolutelyPositionedChildElement && parentElementPosition === 'relative' && isParentOverflowVisible
+ // +---------------------------+
+ // | parent |
+ // | (position: relative;) |
+ // | (overflow: visible;) |
+ // | +-----------+---------------+
+ // | | child |
+ // | | (position: absolute;) |
+ // | +-----------+---------------+
+ // +---------------------------+
+ //
+ // condition: absolutelyPositionedChildElement && parentElementPosition !== 'relative'
+ // +---------------------------+
+ // | parent |
+ // | (position: static;) |
+ // | +-----------+---------------+
+ // | | child |
+ // | | (position: absolute;) |
+ // | +-----------+---------------+
+ // +---------------------------+
+ if (isParentOverflowVisible ||
+ absolutelyPositionedChildElement && ((parentElementPosition === 'relative' && isParentOverflowVisible) ||
+ parentElementPosition !== 'relative')) {
+ child = parent;
+ parent = parent.parentNode;
+ continue;
+ }
+ const parentRect = new Rect(parent);
+ const intersectionRect = visibleRect.getIntersection(parentRect);
+ if (intersectionRect) {
+ if (intersectionRect.getArea() < visibleRect.getArea()) {
+ // Reduce the visible rect to the intersection.
+ visibleRect = intersectionRect;
+ }
+ }
+ else {
+ // There's no intersection, the rect is completely invisible.
+ return null;
+ }
+ child = parent;
+ parent = parent.parentNode;
+ }
+ return visibleRect;
+ }
+ /**
+ * Checks if all property values ({@link #top}, {@link #left}, {@link #right},
+ * {@link #bottom}, {@link #width} and {@link #height}) are the equal in both rect
+ * instances.
+ *
+ * @param anotherRect A rect instance to compare with.
+ * @returns `true` when Rects are equal. `false` otherwise.
+ */
+ isEqual(anotherRect) {
+ for (const prop of rectProperties) {
+ if (this[prop] !== anotherRect[prop]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ /**
+ * Checks whether a rect fully contains another rect instance.
+ *
+ * @param anotherRect
+ * @returns `true` if contains, `false` otherwise.
+ */
+ contains(anotherRect) {
+ const intersectRect = this.getIntersection(anotherRect);
+ return !!(intersectRect && intersectRect.isEqual(anotherRect));
+ }
+ /**
+ * Recalculates screen coordinates to coordinates relative to the positioned ancestor offset.
+ */
+ toAbsoluteRect() {
+ const { scrollX, scrollY } = global.window;
+ const absoluteRect = this.clone().moveBy(scrollX, scrollY);
+ if (isDomElement(absoluteRect._source)) {
+ const positionedAncestor = getPositionedAncestor(absoluteRect._source);
+ if (positionedAncestor) {
+ shiftRectToCompensatePositionedAncestor(absoluteRect, positionedAncestor);
+ }
+ }
+ return absoluteRect;
+ }
+ /**
+ * Excludes scrollbars and CSS borders from the rect.
+ *
+ * * Borders are removed when {@link #_source} is an HTML element.
+ * * Scrollbars are excluded from HTML elements and the `window`.
+ *
+ * @returns A rect which has been updated.
+ */
+ excludeScrollbarsAndBorders() {
+ const source = this._source;
+ let scrollBarWidth, scrollBarHeight, direction;
+ if (isWindow(source)) {
+ scrollBarWidth = source.innerWidth - source.document.documentElement.clientWidth;
+ scrollBarHeight = source.innerHeight - source.document.documentElement.clientHeight;
+ direction = source.getComputedStyle(source.document.documentElement).direction;
+ }
+ else {
+ const borderWidths = getBorderWidths(source);
+ scrollBarWidth = source.offsetWidth - source.clientWidth - borderWidths.left - borderWidths.right;
+ scrollBarHeight = source.offsetHeight - source.clientHeight - borderWidths.top - borderWidths.bottom;
+ direction = source.ownerDocument.defaultView.getComputedStyle(source).direction;
+ this.left += borderWidths.left;
+ this.top += borderWidths.top;
+ this.right -= borderWidths.right;
+ this.bottom -= borderWidths.bottom;
+ this.width = this.right - this.left;
+ this.height = this.bottom - this.top;
+ }
+ this.width -= scrollBarWidth;
+ if (direction === 'ltr') {
+ this.right -= scrollBarWidth;
+ }
+ else {
+ this.left += scrollBarWidth;
+ }
+ this.height -= scrollBarHeight;
+ this.bottom -= scrollBarHeight;
+ return this;
+ }
+ /**
+ * Returns an array of rects of the given native DOM Range.
+ *
+ * @param range A native DOM range.
+ * @returns DOM Range rects.
+ */
+ static getDomRangeRects(range) {
+ const rects = [];
+ // Safari does not iterate over ClientRectList using for...of loop.
+ const clientRects = Array.from(range.getClientRects());
+ if (clientRects.length) {
+ for (const rect of clientRects) {
+ rects.push(new Rect(rect));
+ }
+ }
+ // If there's no client rects for the Range, use parent container's bounding rect
+ // instead and adjust rect's width to simulate the actual geometry of such range.
+ // https://github.com/ckeditor/ckeditor5-utils/issues/153
+ // https://github.com/ckeditor/ckeditor5-ui/issues/317
+ else {
+ let startContainer = range.startContainer;
+ if (isText(startContainer)) {
+ startContainer = startContainer.parentNode;
+ }
+ const rect = new Rect(startContainer.getBoundingClientRect());
+ rect.right = rect.left;
+ rect.width = 0;
+ rects.push(rect);
+ }
+ return rects;
+ }
+ /**
+ * Returns a bounding rectangle that contains all the given `rects`.
+ *
+ * @param rects A list of rectangles that should be contained in the result rectangle.
+ * @returns Bounding rectangle or `null` if no `rects` were given.
+ */
+ static getBoundingRect(rects) {
+ const boundingRectData = {
+ left: Number.POSITIVE_INFINITY,
+ top: Number.POSITIVE_INFINITY,
+ right: Number.NEGATIVE_INFINITY,
+ bottom: Number.NEGATIVE_INFINITY,
+ width: 0,
+ height: 0
+ };
+ let rectangleCount = 0;
+ for (const rect of rects) {
+ rectangleCount++;
+ boundingRectData.left = Math.min(boundingRectData.left, rect.left);
+ boundingRectData.top = Math.min(boundingRectData.top, rect.top);
+ boundingRectData.right = Math.max(boundingRectData.right, rect.right);
+ boundingRectData.bottom = Math.max(boundingRectData.bottom, rect.bottom);
+ }
+ if (rectangleCount == 0) {
+ return null;
+ }
+ boundingRectData.width = boundingRectData.right - boundingRectData.left;
+ boundingRectData.height = boundingRectData.bottom - boundingRectData.top;
+ return new Rect(boundingRectData);
+ }
+}
+/**
+ * Acquires all the rect properties from the passed source.
+ */
+function copyRectProperties(rect, source) {
+ for (const p of rectProperties) {
+ rect[p] = source[p];
+ }
+}
+/**
+ * Checks if provided object is a HTML element.
+ */
+function isBody(value) {
+ if (!isDomElement(value)) {
+ return false;
+ }
+ return value === value.ownerDocument.body;
+}
+/**
+ * Checks if provided object "looks like" a DOM Element and has API required by `Rect` class.
+ */
+function isDomElement(value) {
+ // Note: earlier we used `isElement()` from lodash library, however that function is less performant because
+ // it makes complicated checks to make sure that given value is a DOM element.
+ return value !== null && typeof value === 'object' && value.nodeType === 1 && typeof value.getBoundingClientRect === 'function';
+}
+/**
+ * Returns the value of the `position` style of an `HTMLElement`.
+ */
+function getElementPosition(element) {
+ return element instanceof HTMLElement ? element.ownerDocument.defaultView.getComputedStyle(element).position : 'static';
+}
+/**
+ * Returns the value of the `overflow` style of an `HTMLElement` or a `Range`.
+ */
+function getElementOverflow(element) {
+ return element instanceof HTMLElement ? element.ownerDocument.defaultView.getComputedStyle(element).overflow : 'visible';
+}
+/**
+ * For a given absolute Rect coordinates object and a positioned element ancestor, it updates its
+ * coordinates that make up for the position and the scroll of the ancestor.
+ *
+ * This is necessary because while Rects (and DOMRects) are relative to the browser's viewport, their coordinates
+ * are used in real–life to position elements with `position: absolute`, which are scoped by any positioned
+ * (and scrollable) ancestors.
+ */
+function shiftRectToCompensatePositionedAncestor(rect, positionedElementAncestor) {
+ const ancestorPosition = new Rect(positionedElementAncestor);
+ const ancestorBorderWidths = getBorderWidths(positionedElementAncestor);
+ let moveX = 0;
+ let moveY = 0;
+ // (https://github.com/ckeditor/ckeditor5-ui-default/issues/126)
+ // If there's some positioned ancestor of the panel, then its `Rect` must be taken into
+ // consideration. `Rect` is always relative to the viewport while `position: absolute` works
+ // with respect to that positioned ancestor.
+ moveX -= ancestorPosition.left;
+ moveY -= ancestorPosition.top;
+ // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
+ // If there's some positioned ancestor of the panel, not only its position must be taken into
+ // consideration (see above) but also its internal scrolls. Scroll have an impact here because `Rect`
+ // is relative to the viewport (it doesn't care about scrolling), while `position: absolute`
+ // must compensate that scrolling.
+ moveX += positionedElementAncestor.scrollLeft;
+ moveY += positionedElementAncestor.scrollTop;
+ // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
+ // If there's some positioned ancestor of the panel, then its `Rect` includes its CSS `borderWidth`
+ // while `position: absolute` positioning does not consider it.
+ // E.g. `{ position: absolute, top: 0, left: 0 }` means upper left corner of the element,
+ // not upper-left corner of its border.
+ moveX -= ancestorBorderWidths.left;
+ moveY -= ancestorBorderWidths.top;
+ rect.moveBy(moveX, moveY);
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.d.ts
new file mode 100644
index 00000000..65e7a44a
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.d.ts
@@ -0,0 +1,13 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/remove
+ */
+/**
+ * Removes given node from parent.
+ *
+ * @param node Node to remove.
+ */
+export default function remove(node: Node): void;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js
new file mode 100644
index 00000000..7942fdca
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js
@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/remove
+ */
+/**
+ * Removes given node from parent.
+ *
+ * @param node Node to remove.
+ */
+export default function remove(node) {
+ const parent = node.parentNode;
+ if (parent) {
+ parent.removeChild(node);
+ }
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.d.ts
new file mode 100644
index 00000000..483d1aab
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.d.ts
@@ -0,0 +1,74 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * A helper class which instances allow performing custom actions when native DOM elements are resized.
+ *
+ * ```ts
+ * const editableElement = editor.editing.view.getDomRoot();
+ *
+ * const observer = new ResizeObserver( editableElement, entry => {
+ * console.log( 'The editable element has been resized in DOM.' );
+ * console.log( entry.target ); // -> editableElement
+ * console.log( entry.contentRect.width ); // -> e.g. '423px'
+ * } );
+ * ```
+ *
+ * It uses the [native DOM resize observer](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
+ * under the hood.
+ */
+export default class ResizeObserver {
+ /**
+ * The element observed by this observer.
+ */
+ private readonly _element;
+ /**
+ * The callback executed each time {@link #_element} is resized.
+ */
+ private readonly _callback;
+ /**
+ * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
+ */
+ private static _observerInstance;
+ /**
+ * A mapping of native DOM elements and their callbacks shared across all
+ * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
+ */
+ private static _elementCallbacks;
+ /**
+ * Creates an instance of the `ResizeObserver` class.
+ *
+ * @param element A DOM element that is to be observed for resizing. Note that
+ * the element must be visible (i.e. not detached from DOM) for the observer to work.
+ * @param callback A function called when the observed element was resized. It passes
+ * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
+ * object with information about the resize event.
+ */
+ constructor(element: Element, callback: (entry: ResizeObserverEntry) => void);
+ /**
+ * The element observed by this observer.
+ */
+ get element(): Element;
+ /**
+ * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
+ */
+ destroy(): void;
+ /**
+ * Registers a new resize callback for the DOM element.
+ */
+ private static _addElementCallback;
+ /**
+ * Removes a resize callback from the DOM element. If no callbacks are left
+ * for the element, it removes the element from the native observer.
+ */
+ private static _deleteElementCallback;
+ /**
+ * Returns are registered resize callbacks for the DOM element.
+ */
+ private static _getElementCallbacks;
+ /**
+ * Creates the single native observer shared across all `ResizeObserver` instances.
+ */
+ private static _createObserver;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js
new file mode 100644
index 00000000..eb5610b9
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js
@@ -0,0 +1,127 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/resizeobserver
+ */
+import global from './global.js';
+/**
+ * A helper class which instances allow performing custom actions when native DOM elements are resized.
+ *
+ * ```ts
+ * const editableElement = editor.editing.view.getDomRoot();
+ *
+ * const observer = new ResizeObserver( editableElement, entry => {
+ * console.log( 'The editable element has been resized in DOM.' );
+ * console.log( entry.target ); // -> editableElement
+ * console.log( entry.contentRect.width ); // -> e.g. '423px'
+ * } );
+ * ```
+ *
+ * It uses the [native DOM resize observer](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
+ * under the hood.
+ */
+class ResizeObserver {
+ /**
+ * Creates an instance of the `ResizeObserver` class.
+ *
+ * @param element A DOM element that is to be observed for resizing. Note that
+ * the element must be visible (i.e. not detached from DOM) for the observer to work.
+ * @param callback A function called when the observed element was resized. It passes
+ * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
+ * object with information about the resize event.
+ */
+ constructor(element, callback) {
+ // **Note**: For the maximum performance, this class ensures only a single instance of the native
+ // observer is used no matter how many instances of this class were created.
+ if (!ResizeObserver._observerInstance) {
+ ResizeObserver._createObserver();
+ }
+ this._element = element;
+ this._callback = callback;
+ ResizeObserver._addElementCallback(element, callback);
+ ResizeObserver._observerInstance.observe(element);
+ }
+ /**
+ * The element observed by this observer.
+ */
+ get element() {
+ return this._element;
+ }
+ /**
+ * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
+ */
+ destroy() {
+ ResizeObserver._deleteElementCallback(this._element, this._callback);
+ }
+ /**
+ * Registers a new resize callback for the DOM element.
+ */
+ static _addElementCallback(element, callback) {
+ if (!ResizeObserver._elementCallbacks) {
+ ResizeObserver._elementCallbacks = new Map();
+ }
+ let callbacks = ResizeObserver._elementCallbacks.get(element);
+ if (!callbacks) {
+ callbacks = new Set();
+ ResizeObserver._elementCallbacks.set(element, callbacks);
+ }
+ callbacks.add(callback);
+ }
+ /**
+ * Removes a resize callback from the DOM element. If no callbacks are left
+ * for the element, it removes the element from the native observer.
+ */
+ static _deleteElementCallback(element, callback) {
+ const callbacks = ResizeObserver._getElementCallbacks(element);
+ // Remove the element callback. Check if exist first in case someone
+ // called destroy() twice.
+ if (callbacks) {
+ callbacks.delete(callback);
+ // If no callbacks left for the element, also remove the element.
+ if (!callbacks.size) {
+ ResizeObserver._elementCallbacks.delete(element);
+ ResizeObserver._observerInstance.unobserve(element);
+ }
+ }
+ if (ResizeObserver._elementCallbacks && !ResizeObserver._elementCallbacks.size) {
+ ResizeObserver._observerInstance = null;
+ ResizeObserver._elementCallbacks = null;
+ }
+ }
+ /**
+ * Returns are registered resize callbacks for the DOM element.
+ */
+ static _getElementCallbacks(element) {
+ if (!ResizeObserver._elementCallbacks) {
+ return null;
+ }
+ return ResizeObserver._elementCallbacks.get(element);
+ }
+ /**
+ * Creates the single native observer shared across all `ResizeObserver` instances.
+ */
+ static _createObserver() {
+ ResizeObserver._observerInstance = new global.window.ResizeObserver(entries => {
+ for (const entry of entries) {
+ const callbacks = ResizeObserver._getElementCallbacks(entry.target);
+ if (callbacks) {
+ for (const callback of callbacks) {
+ callback(entry);
+ }
+ }
+ }
+ });
+ }
+}
+/**
+ * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
+ */
+ResizeObserver._observerInstance = null;
+/**
+ * A mapping of native DOM elements and their callbacks shared across all
+ * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
+ */
+ResizeObserver._elementCallbacks = null;
+export default ResizeObserver;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.d.ts
new file mode 100644
index 00000000..c2a62898
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.d.ts
@@ -0,0 +1,73 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+type IfTrue = T extends true ? true : never;
+/**
+ * Makes any page `HTMLElement` or `Range` (`target`) visible inside the browser viewport.
+ * This helper will scroll all `target` ancestors and the web browser viewport to reveal the target to
+ * the user. If the `target` is already visible, nothing will happen.
+ *
+ * @param options Additional configuration of the scrolling behavior.
+ * @param options.target A target, which supposed to become visible to the user.
+ * @param options.viewportOffset An offset from the edge of the viewport (in pixels)
+ * the `target` will be moved by if the viewport is scrolled. It enhances the user experience
+ * by keeping the `target` some distance from the edge of the viewport and thus making it easier to
+ * read or edit by the user.
+ * @param options.ancestorOffset An offset from the boundary of scrollable ancestors (if any)
+ * the `target` will be moved by if the viewport is scrolled. It enhances the user experience
+ * by keeping the `target` some distance from the edge of the ancestors and thus making it easier to
+ * read or edit by the user.
+ * @param options.alignToTop When set `true`, the helper will make sure the `target` is scrolled up
+ * to the top boundary of the viewport and/or scrollable ancestors if scrolled up. When not set
+ * (default), the `target` will be revealed by scrolling as little as possible. This option will
+ * not affect `targets` that must be scrolled down because they will appear at the top of the boundary
+ * anyway.
+ *
+ * ```
+ * scrollViewportToShowTarget() with scrollViewportToShowTarget() with
+ * Initial state alignToTop unset (default) alignToTop = true
+ *
+ * ┌────────────────────────────────┬─┐ ┌────────────────────────────────┬─┐ ┌────────────────────────────────┬─┐
+ * │ │▲│ │ │▲│ │ [ Target to be revealed ] │▲│
+ * │ │ │ │ │ │ │ │ │
+ * │ │█│ │ │ │ │ │ │
+ * │ │█│ │ │ │ │ │ │
+ * │ │ │ │ │█│ │ │ │
+ * │ │ │ │ │█│ │ │█│
+ * │ │ │ │ │ │ │ │█│
+ * │ │▼│ │ [ Target to be revealed ] │▼│ │ │▼│
+ * └────────────────────────────────┴─┘ └────────────────────────────────┴─┘ └────────────────────────────────┴─┘
+ *
+ *
+ * [ Target to be revealed ]
+ *```
+ *
+ * @param options.forceScroll When set `true`, the `target` will be aligned to the top of the viewport
+ * and scrollable ancestors whether it is already visible or not. This option will only work when `alignToTop`
+ * is `true`
+ */
+export declare function scrollViewportToShowTarget>({ target, viewportOffset, ancestorOffset, alignToTop, forceScroll }: {
+ readonly target: HTMLElement | Range;
+ readonly viewportOffset?: number | {
+ top: number;
+ bottom: number;
+ left: number;
+ right: number;
+ };
+ readonly ancestorOffset?: number;
+ readonly alignToTop?: T;
+ readonly forceScroll?: U;
+}): void;
+/**
+ * Makes any page `HTMLElement` or `Range` (target) visible within its scrollable ancestors,
+ * e.g. if they have `overflow: scroll` CSS style.
+ *
+ * @param target A target, which supposed to become visible to the user.
+ * @param ancestorOffset An offset between the target and the boundary of scrollable ancestors
+ * to be maintained while scrolling.
+ * @param limiterElement The outermost ancestor that should be scrolled. If specified, it can prevent
+ * scrolling the whole page.
+ */
+export declare function scrollAncestorsToShowTarget(target: HTMLElement | Range, ancestorOffset?: number, limiterElement?: HTMLElement): void;
+export {};
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js
new file mode 100644
index 00000000..1a09341c
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js
@@ -0,0 +1,383 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module utils/dom/scroll
+ */
+import isRange from './isrange.js';
+import Rect from './rect.js';
+import isText from './istext.js';
+/**
+ * Makes any page `HTMLElement` or `Range` (`target`) visible inside the browser viewport.
+ * This helper will scroll all `target` ancestors and the web browser viewport to reveal the target to
+ * the user. If the `target` is already visible, nothing will happen.
+ *
+ * @param options Additional configuration of the scrolling behavior.
+ * @param options.target A target, which supposed to become visible to the user.
+ * @param options.viewportOffset An offset from the edge of the viewport (in pixels)
+ * the `target` will be moved by if the viewport is scrolled. It enhances the user experience
+ * by keeping the `target` some distance from the edge of the viewport and thus making it easier to
+ * read or edit by the user.
+ * @param options.ancestorOffset An offset from the boundary of scrollable ancestors (if any)
+ * the `target` will be moved by if the viewport is scrolled. It enhances the user experience
+ * by keeping the `target` some distance from the edge of the ancestors and thus making it easier to
+ * read or edit by the user.
+ * @param options.alignToTop When set `true`, the helper will make sure the `target` is scrolled up
+ * to the top boundary of the viewport and/or scrollable ancestors if scrolled up. When not set
+ * (default), the `target` will be revealed by scrolling as little as possible. This option will
+ * not affect `targets` that must be scrolled down because they will appear at the top of the boundary
+ * anyway.
+ *
+ * ```
+ * scrollViewportToShowTarget() with scrollViewportToShowTarget() with
+ * Initial state alignToTop unset (default) alignToTop = true
+ *
+ * ┌────────────────────────────────┬─┐ ┌────────────────────────────────┬─┐ ┌────────────────────────────────┬─┐
+ * │ │▲│ │ │▲│ │ [ Target to be revealed ] │▲│
+ * │ │ │ │ │ │ │ │ │
+ * │ │█│ │ │ │ │ │ │
+ * │ │█│ │ │ │ │ │ │
+ * │ │ │ │ │█│ │ │ │
+ * │ │ │ │ │█│ │ │█│
+ * │ │ │ │ │ │ │ │█│
+ * │ │▼│ │ [ Target to be revealed ] │▼│ │ │▼│
+ * └────────────────────────────────┴─┘ └────────────────────────────────┴─┘ └────────────────────────────────┴─┘
+ *
+ *
+ * [ Target to be revealed ]
+ *```
+ *
+ * @param options.forceScroll When set `true`, the `target` will be aligned to the top of the viewport
+ * and scrollable ancestors whether it is already visible or not. This option will only work when `alignToTop`
+ * is `true`
+ */
+export function scrollViewportToShowTarget({ target, viewportOffset = 0, ancestorOffset = 0, alignToTop, forceScroll }) {
+ const targetWindow = getWindow(target);
+ let currentWindow = targetWindow;
+ let currentFrame = null;
+ viewportOffset = normalizeViewportOffset(viewportOffset);
+ // Iterate over all windows, starting from target's parent window up to window#top.
+ while (currentWindow) {
+ let firstAncestorToScroll;
+ // Let's scroll target's ancestors first to reveal it. Then, once the ancestor scrolls
+ // settled down, the algorithm can eventually scroll the viewport of the current window.
+ //
+ // Note: If the current window is target's **original** window (e.g. the first one),
+ // start scrolling the closest parent of the target. If not, scroll the closest parent
+ // of an iframe that resides in the current window.
+ if (currentWindow == targetWindow) {
+ firstAncestorToScroll = getParentElement(target);
+ }
+ else {
+ firstAncestorToScroll = getParentElement(currentFrame);
+ }
+ // Scroll the target's ancestors first. Once done, scrolling the viewport is easy.
+ scrollAncestorsToShowRect({
+ parent: firstAncestorToScroll,
+ getRect: () => {
+ // Note: If the target does not belong to the current window **directly**,
+ // i.e. it resides in an iframe belonging to the window, obtain the target's rect
+ // in the coordinates of the current window. By default, a Rect returns geometry
+ // relative to the current window's viewport. To make it work in a parent window,
+ // it must be shifted.
+ return getRectRelativeToWindow(target, currentWindow);
+ },
+ alignToTop,
+ ancestorOffset,
+ forceScroll
+ });
+ // Obtain the rect of the target after it has been scrolled within its ancestors.
+ // It's time to scroll the viewport.
+ const targetRect = getRectRelativeToWindow(target, currentWindow);
+ scrollWindowToShowRect({
+ window: currentWindow,
+ rect: targetRect,
+ viewportOffset,
+ alignToTop,
+ forceScroll
+ });
+ if (currentWindow.parent != currentWindow) {
+ // Keep the reference to the