pull main
This commit is contained in:
commit
3d4860c7a5
|
|
@ -11,7 +11,6 @@ const TicketingDetailPage = async () => {
|
|||
<SiteBreadcrumb />
|
||||
<div className="space-y-4">
|
||||
{/* <FormDetailTicketing id={selectedTicketId} /> */}
|
||||
<></>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -76,12 +76,12 @@ function CustomEditor(props) {
|
|||
.ckeditor-wrapper {
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
// box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
|
||||
0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.ckeditor-wrapper :global(.ck.ck-editor__main) {
|
||||
min-height: ${props.height || 300}px;
|
||||
min-height: ${props.height || 400}px;
|
||||
max-height: ${maxHeight}px;
|
||||
}
|
||||
|
||||
|
|
@ -168,4 +168,4 @@ function CustomEditor(props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default CustomEditor;
|
||||
export default CustomEditor;
|
||||
|
|
@ -724,28 +724,9 @@ export default function FormDetailTicketing({ id }: Props) {
|
|||
</div>
|
||||
|
||||
{/* Chat Messages */}
|
||||
<div className="flex-1 overflow-auto p-4 space-y-3 bg-gray-50">
|
||||
{!detail ? (
|
||||
<div className="h-full flex items-center justify-center text-muted-foreground">
|
||||
<div className="text-center">
|
||||
<div className="mb-4">
|
||||
<svg
|
||||
width="72"
|
||||
height="72"
|
||||
viewBox="0 0 24 24"
|
||||
className="mx-auto opacity-60"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 3C7 3 3 6.6 3 11c0 1.9.8 3.6 2.2 5v3.1L8 17.9c1 .3 2 .5 4 .5 5 0 9-3.6 9-8.1S17 3 12 3z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-sm">Pilih issue untuk melihat detail</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
ticketReply.map((m) => {
|
||||
<div className="flex-1 overflow-auto p-4 bg-gray-50 flex flex-col-reverse">
|
||||
<div className="flex flex-col gap-3">
|
||||
{ticketReply.map((m) => {
|
||||
const isUser = m.user.fullname !== "Agent";
|
||||
return (
|
||||
<div
|
||||
|
|
@ -777,9 +758,8 @@ export default function FormDetailTicketing({ id }: Props) {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
<div ref={chatEndRef} />
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Box */}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.d.ts
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js
generated
vendored
Normal file
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js
generated
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.d.ts
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js
generated
vendored
Normal file
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js
generated
vendored
Normal file
|
|
@ -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]';
|
||||
}
|
||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.d.ts
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js
generated
vendored
Normal file
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js
generated
vendored
Normal file
|
|
@ -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]';
|
||||
}
|
||||
10
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.d.ts
generated
vendored
Normal file
10
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js
generated
vendored
Normal file
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js
generated
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
18
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.d.ts
generated
vendored
Normal file
18
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js
generated
vendored
Normal file
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js
generated
vendored
Normal file
|
|
@ -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);
|
||||
}
|
||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.d.ts
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js
generated
vendored
Normal file
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js
generated
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
211
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.d.ts
generated
vendored
Normal file
211
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.d.ts
generated
vendored
Normal file
|
|
@ -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 <body>.
|
||||
* 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<PositioningFunction>;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
313
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js
generated
vendored
Normal file
313
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js
generated
vendored
Normal file
|
|
@ -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 <body>.
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
195
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.d.ts
generated
vendored
Normal file
195
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.d.ts
generated
vendored
Normal file
|
|
@ -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<Rect>;
|
||||
/**
|
||||
* 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>): 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;
|
||||
}
|
||||
474
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js
generated
vendored
Normal file
474
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js
generated
vendored
Normal file
|
|
@ -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 <body> 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 <body>.
|
||||
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 <body> 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);
|
||||
}
|
||||
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.d.ts
generated
vendored
Normal file
13
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
18
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js
generated
vendored
Normal file
18
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js
generated
vendored
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
74
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.d.ts
generated
vendored
Normal file
74
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.d.ts
generated
vendored
Normal file
|
|
@ -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;
|
||||
}
|
||||
127
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js
generated
vendored
Normal file
127
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js
generated
vendored
Normal file
|
|
@ -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;
|
||||
73
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.d.ts
generated
vendored
Normal file
73
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.d.ts
generated
vendored
Normal file
|
|
@ -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> = 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<T extends boolean, U extends IfTrue<T>>({ 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 {};
|
||||
383
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js
generated
vendored
Normal file
383
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js
generated
vendored
Normal file
|
|
@ -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 <iframe> element the "previous current window" was
|
||||
// rendered within. It will be useful to re–calculate the rect of the target
|
||||
// in the parent window's relative geometry. The target's rect must be shifted
|
||||
// by it's iframe's position.
|
||||
currentFrame = currentWindow.frameElement;
|
||||
currentWindow = currentWindow.parent;
|
||||
// If the current window has some parent but frameElement is inaccessible, then they have
|
||||
// different domains/ports and, due to security reasons, accessing and scrolling
|
||||
// the parent window won't be possible.
|
||||
// See https://github.com/ckeditor/ckeditor5/issues/930.
|
||||
if (!currentFrame) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
currentWindow = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 function scrollAncestorsToShowTarget(target, ancestorOffset, limiterElement) {
|
||||
const targetParent = getParentElement(target);
|
||||
scrollAncestorsToShowRect({
|
||||
parent: targetParent,
|
||||
getRect: () => new Rect(target),
|
||||
ancestorOffset,
|
||||
limiterElement
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Makes a given rect visible within its parent window.
|
||||
*
|
||||
* Note: Avoid the situation where the caret is still in the viewport, but totally
|
||||
* at the edge of it. In such situation, if it moved beyond the viewport in the next
|
||||
* action e.g. after paste, the scrolling would move it to the viewportOffset level
|
||||
* and it all would look like the caret visually moved up/down:
|
||||
*
|
||||
* 1.
|
||||
* ```
|
||||
* | foo[]
|
||||
* | <--- N px of space below the caret
|
||||
* +---------------------------------...
|
||||
* ```
|
||||
*
|
||||
* 2. *paste*
|
||||
* 3.
|
||||
* ```
|
||||
* |
|
||||
* |
|
||||
* +-foo-----------------------------...
|
||||
* bar[] <--- caret below viewport, scrolling...
|
||||
* ```
|
||||
*
|
||||
* 4. *scrolling*
|
||||
* 5.
|
||||
* ```
|
||||
* |
|
||||
* | foo
|
||||
* | bar[] <--- caret precisely at the edge
|
||||
* +---------------------------------...
|
||||
* ```
|
||||
*
|
||||
* To prevent this, this method checks the rects moved by the viewportOffset to cover
|
||||
* the upper/lower edge of the viewport. It makes sure if the action repeats, there's
|
||||
* no twitching – it's a purely visual improvement:
|
||||
*
|
||||
* 5. (after fix)
|
||||
* ```
|
||||
* |
|
||||
* | foo
|
||||
* | bar[]
|
||||
* | <--- N px of space below the caret
|
||||
* +---------------------------------...
|
||||
* ```
|
||||
*
|
||||
* @param options Additional configuration of the scrolling behavior.
|
||||
* @param options.window A window which is scrolled to reveal the rect.
|
||||
* @param options.rect A rect which is to be revealed.
|
||||
* @param options.viewportOffset An offset from the edge of the viewport (in pixels) the `rect` will be
|
||||
* moved by if the viewport is scrolled.
|
||||
* @param options.alignToTop When set `true`, the helper will make sure the `rect` is scrolled up
|
||||
* to the top boundary of the viewport if scrolled up. When not set (default), the `rect` will be
|
||||
* revealed by scrolling as little as possible. This option will not affect rects that must be scrolled
|
||||
* down because they will appear at the top of the boundary anyway.
|
||||
* @param options.forceScroll When set `true`, the `rect` will be aligned to the top of the viewport
|
||||
* whether it is already visible or not. This option will only work when `alignToTop` is `true`
|
||||
*/
|
||||
function scrollWindowToShowRect({ window, rect, alignToTop, forceScroll, viewportOffset }) {
|
||||
const targetShiftedDownRect = rect.clone().moveBy(0, viewportOffset.bottom);
|
||||
const targetShiftedUpRect = rect.clone().moveBy(0, -viewportOffset.top);
|
||||
const viewportRect = new Rect(window).excludeScrollbarsAndBorders();
|
||||
const rects = [targetShiftedUpRect, targetShiftedDownRect];
|
||||
const forceScrollToTop = alignToTop && forceScroll;
|
||||
const allRectsFitInViewport = rects.every(rect => viewportRect.contains(rect));
|
||||
let { scrollX, scrollY } = window;
|
||||
const initialScrollX = scrollX;
|
||||
const initialScrollY = scrollY;
|
||||
if (forceScrollToTop) {
|
||||
scrollY -= (viewportRect.top - rect.top) + viewportOffset.top;
|
||||
}
|
||||
else if (!allRectsFitInViewport) {
|
||||
if (isAbove(targetShiftedUpRect, viewportRect)) {
|
||||
scrollY -= viewportRect.top - rect.top + viewportOffset.top;
|
||||
}
|
||||
else if (isBelow(targetShiftedDownRect, viewportRect)) {
|
||||
if (alignToTop) {
|
||||
scrollY += rect.top - viewportRect.top - viewportOffset.top;
|
||||
}
|
||||
else {
|
||||
scrollY += rect.bottom - viewportRect.bottom + viewportOffset.bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allRectsFitInViewport) {
|
||||
// TODO: Web browsers scroll natively to place the target in the middle
|
||||
// of the viewport. It's not a very popular case, though.
|
||||
if (isLeftOf(rect, viewportRect)) {
|
||||
scrollX -= viewportRect.left - rect.left + viewportOffset.left;
|
||||
}
|
||||
else if (isRightOf(rect, viewportRect)) {
|
||||
scrollX += rect.right - viewportRect.right + viewportOffset.right;
|
||||
}
|
||||
}
|
||||
if (scrollX != initialScrollX || scrollY !== initialScrollY) {
|
||||
window.scrollTo(scrollX, scrollY);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Recursively scrolls element ancestors to visually reveal a rect.
|
||||
*
|
||||
* @param options Additional configuration of the scrolling behavior.
|
||||
* @param options.parent The first parent ancestor to start scrolling.
|
||||
* @param options.getRect A function which returns the Rect, which is to be revealed.
|
||||
* @param options.ancestorOffset An offset from the boundary of scrollable ancestors (if any)
|
||||
* the `Rect` instance will be moved by if the viewport is scrolled.
|
||||
* @param options.alignToTop When set `true`, the helper will make sure the `Rect` instance is scrolled up
|
||||
* to the top boundary of the scrollable ancestors if scrolled up. When not set (default), the `rect`
|
||||
* will be revealed by scrolling as little as possible. This option will not affect rects that must be
|
||||
* scrolled down because they will appear at the top of the boundary
|
||||
* anyway.
|
||||
* @param options.forceScroll When set `true`, the `rect` will be aligned to the top of scrollable ancestors
|
||||
* whether it is already visible or not. This option will only work when `alignToTop` is `true`
|
||||
* @param options.limiterElement The outermost ancestor that should be scrolled. Defaults to the `<body>` element.
|
||||
*/
|
||||
function scrollAncestorsToShowRect({ parent, getRect, alignToTop, forceScroll, ancestorOffset = 0, limiterElement }) {
|
||||
const parentWindow = getWindow(parent);
|
||||
const forceScrollToTop = alignToTop && forceScroll;
|
||||
let parentRect, targetRect, targetFitsInTarget;
|
||||
const limiter = limiterElement || parentWindow.document.body;
|
||||
while (parent != limiter) {
|
||||
targetRect = getRect();
|
||||
parentRect = new Rect(parent).excludeScrollbarsAndBorders();
|
||||
targetFitsInTarget = parentRect.contains(targetRect);
|
||||
if (forceScrollToTop) {
|
||||
parent.scrollTop -= (parentRect.top - targetRect.top) + ancestorOffset;
|
||||
}
|
||||
else if (!targetFitsInTarget) {
|
||||
if (isAbove(targetRect, parentRect)) {
|
||||
parent.scrollTop -= parentRect.top - targetRect.top + ancestorOffset;
|
||||
}
|
||||
else if (isBelow(targetRect, parentRect)) {
|
||||
if (alignToTop) {
|
||||
parent.scrollTop += targetRect.top - parentRect.top - ancestorOffset;
|
||||
}
|
||||
else {
|
||||
parent.scrollTop += targetRect.bottom - parentRect.bottom + ancestorOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!targetFitsInTarget) {
|
||||
if (isLeftOf(targetRect, parentRect)) {
|
||||
parent.scrollLeft -= parentRect.left - targetRect.left + ancestorOffset;
|
||||
}
|
||||
else if (isRightOf(targetRect, parentRect)) {
|
||||
parent.scrollLeft += targetRect.right - parentRect.right + ancestorOffset;
|
||||
}
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines if a given `Rect` extends beyond the bottom edge of the second `Rect`.
|
||||
*/
|
||||
function isBelow(firstRect, secondRect) {
|
||||
return firstRect.bottom > secondRect.bottom;
|
||||
}
|
||||
/**
|
||||
* Determines if a given `Rect` extends beyond the top edge of the second `Rect`.
|
||||
*/
|
||||
function isAbove(firstRect, secondRect) {
|
||||
return firstRect.top < secondRect.top;
|
||||
}
|
||||
/**
|
||||
* Determines if a given `Rect` extends beyond the left edge of the second `Rect`.
|
||||
*/
|
||||
function isLeftOf(firstRect, secondRect) {
|
||||
return firstRect.left < secondRect.left;
|
||||
}
|
||||
/**
|
||||
* Determines if a given `Rect` extends beyond the right edge of the second `Rect`.
|
||||
*/
|
||||
function isRightOf(firstRect, secondRect) {
|
||||
return firstRect.right > secondRect.right;
|
||||
}
|
||||
/**
|
||||
* Returns the closest window of an element or range.
|
||||
*/
|
||||
function getWindow(elementOrRange) {
|
||||
if (isRange(elementOrRange)) {
|
||||
return elementOrRange.startContainer.ownerDocument.defaultView;
|
||||
}
|
||||
else {
|
||||
return elementOrRange.ownerDocument.defaultView;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the closest parent of an element or DOM range.
|
||||
*/
|
||||
function getParentElement(elementOrRange) {
|
||||
if (isRange(elementOrRange)) {
|
||||
let parent = elementOrRange.commonAncestorContainer;
|
||||
// If a Range is attached to the Text, use the closest element ancestor.
|
||||
if (isText(parent)) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
else {
|
||||
return elementOrRange.parentNode;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the rect of an element or range residing in an iframe.
|
||||
* The result rect is relative to the geometry of the passed window instance.
|
||||
*
|
||||
* @param target Element or range which rect should be returned.
|
||||
* @param relativeWindow A window the rect should be relative to.
|
||||
*/
|
||||
function getRectRelativeToWindow(target, relativeWindow) {
|
||||
const targetWindow = getWindow(target);
|
||||
const rect = new Rect(target);
|
||||
if (targetWindow === relativeWindow) {
|
||||
return rect;
|
||||
}
|
||||
else {
|
||||
let currentWindow = targetWindow;
|
||||
while (currentWindow != relativeWindow) {
|
||||
const frame = currentWindow.frameElement;
|
||||
const frameRect = new Rect(frame).excludeScrollbarsAndBorders();
|
||||
rect.moveBy(frameRect.left, frameRect.top);
|
||||
currentWindow = currentWindow.parent;
|
||||
}
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
/**
|
||||
* A helper that explodes the `viewportOffset` configuration if defined as a plain number into an object
|
||||
* with `top`, `bottom`, `left`, and `right` properties.
|
||||
*
|
||||
* If an object value is passed, this helper will pass it through.
|
||||
*
|
||||
* @param viewportOffset Viewport offset to be normalized.
|
||||
*/
|
||||
function normalizeViewportOffset(viewportOffset) {
|
||||
if (typeof viewportOffset === 'number') {
|
||||
return {
|
||||
top: viewportOffset,
|
||||
bottom: viewportOffset,
|
||||
left: viewportOffset,
|
||||
right: viewportOffset
|
||||
};
|
||||
}
|
||||
return viewportOffset;
|
||||
}
|
||||
14
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/setdatainelement.d.ts
generated
vendored
Normal file
14
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/setdatainelement.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @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/setdatainelement
|
||||
*/
|
||||
/**
|
||||
* Sets data in a given element.
|
||||
*
|
||||
* @param el The element in which the data will be set.
|
||||
* @param data The data string.
|
||||
*/
|
||||
export default function setDataInElement(el: HTMLElement, data: string): void;
|
||||
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/setdatainelement.js
generated
vendored
Normal file
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/setdatainelement.js
generated
vendored
Normal file
|
|
@ -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/setdatainelement
|
||||
*/
|
||||
/* globals HTMLTextAreaElement */
|
||||
/**
|
||||
* Sets data in a given element.
|
||||
*
|
||||
* @param el The element in which the data will be set.
|
||||
* @param data The data string.
|
||||
*/
|
||||
export default function setDataInElement(el, data) {
|
||||
if (el instanceof HTMLTextAreaElement) {
|
||||
el.value = data;
|
||||
}
|
||||
el.innerHTML = data;
|
||||
}
|
||||
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/tounit.d.ts
generated
vendored
Normal file
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/tounit.d.ts
generated
vendored
Normal file
|
|
@ -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/tounit
|
||||
*/
|
||||
/**
|
||||
* Returns a helper function, which adds a desired trailing
|
||||
* `unit` to the passed value.
|
||||
*
|
||||
* @param unit An unit like "px" or "em".
|
||||
*/
|
||||
export default function toUnit(unit: string): ToUnitHelper;
|
||||
/**
|
||||
* A function, which adds a pre–defined trailing `unit`
|
||||
* to the passed `value`.
|
||||
*
|
||||
* @param value A value to be given the unit.
|
||||
* @returns A value with the trailing unit.
|
||||
*/
|
||||
export type ToUnitHelper = (value: string | number) => string;
|
||||
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/tounit.js
generated
vendored
Normal file
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/dom/tounit.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @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/tounit
|
||||
*/
|
||||
/**
|
||||
* Returns a helper function, which adds a desired trailing
|
||||
* `unit` to the passed value.
|
||||
*
|
||||
* @param unit An unit like "px" or "em".
|
||||
*/
|
||||
export default function toUnit(unit) {
|
||||
return value => value + unit;
|
||||
}
|
||||
31
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/elementreplacer.d.ts
generated
vendored
Normal file
31
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/elementreplacer.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @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/elementreplacer
|
||||
*/
|
||||
/**
|
||||
* Utility class allowing to hide existing HTML elements or replace them with given ones in a way that doesn't remove
|
||||
* the original elements from the DOM.
|
||||
*/
|
||||
export default class ElementReplacer {
|
||||
/**
|
||||
* The elements replaced by {@link #replace} and their replacements.
|
||||
*/
|
||||
private _replacedElements;
|
||||
constructor();
|
||||
/**
|
||||
* Hides the `element` and, if specified, inserts the the given element next to it.
|
||||
*
|
||||
* The effect of this method can be reverted by {@link #restore}.
|
||||
*
|
||||
* @param element The element to replace.
|
||||
* @param newElement The replacement element. If not passed, then the `element` will just be hidden.
|
||||
*/
|
||||
replace(element: HTMLElement, newElement?: HTMLElement): void;
|
||||
/**
|
||||
* Restores what {@link #replace} did.
|
||||
*/
|
||||
restore(): void;
|
||||
}
|
||||
43
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/elementreplacer.js
generated
vendored
Normal file
43
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/elementreplacer.js
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @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/elementreplacer
|
||||
*/
|
||||
/**
|
||||
* Utility class allowing to hide existing HTML elements or replace them with given ones in a way that doesn't remove
|
||||
* the original elements from the DOM.
|
||||
*/
|
||||
export default class ElementReplacer {
|
||||
constructor() {
|
||||
this._replacedElements = [];
|
||||
}
|
||||
/**
|
||||
* Hides the `element` and, if specified, inserts the the given element next to it.
|
||||
*
|
||||
* The effect of this method can be reverted by {@link #restore}.
|
||||
*
|
||||
* @param element The element to replace.
|
||||
* @param newElement The replacement element. If not passed, then the `element` will just be hidden.
|
||||
*/
|
||||
replace(element, newElement) {
|
||||
this._replacedElements.push({ element, newElement });
|
||||
element.style.display = 'none';
|
||||
if (newElement) {
|
||||
element.parentNode.insertBefore(newElement, element.nextSibling);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Restores what {@link #replace} did.
|
||||
*/
|
||||
restore() {
|
||||
this._replacedElements.forEach(({ element, newElement }) => {
|
||||
element.style.display = '';
|
||||
if (newElement) {
|
||||
newElement.remove();
|
||||
}
|
||||
});
|
||||
this._replacedElements = [];
|
||||
}
|
||||
}
|
||||
312
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/emittermixin.d.ts
generated
vendored
Normal file
312
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/emittermixin.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/**
|
||||
* @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/emittermixin
|
||||
*/
|
||||
import EventInfo from '@ckeditor/ckeditor5-utils/src/eventinfo.js';
|
||||
import { type PriorityString } from '@ckeditor/ckeditor5-utils/src/priorities.js';
|
||||
import type { Constructor, Mixed } from '@ckeditor/ckeditor5-utils/src/mix.js';
|
||||
import '@ckeditor/ckeditor5-utils/src/version.js';
|
||||
/**
|
||||
* Mixin that injects the {@link ~Emitter events API} into its host.
|
||||
*
|
||||
* This function creates a class that inherits from the provided `base` and implements `Emitter` interface.
|
||||
*
|
||||
* ```ts
|
||||
* class BaseClass { ... }
|
||||
*
|
||||
* class MyClass extends EmitterMixin( BaseClass ) {
|
||||
* // This class derives from `BaseClass` and implements the `Emitter` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the concept of emitters in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide.
|
||||
* * {@glink framework/deep-dive/event-system Event system} deep-dive guide.
|
||||
*
|
||||
* @label EXTENDS
|
||||
*/
|
||||
export default function EmitterMixin<Base extends Constructor>(base: Base): Mixed<Base, Emitter>;
|
||||
/**
|
||||
* Mixin that injects the {@link ~Emitter events API} into its host.
|
||||
*
|
||||
* This function creates a class that implements `Emitter` interface.
|
||||
*
|
||||
* ```ts
|
||||
* class MyClass extends EmitterMixin() {
|
||||
* // This class implements the `Emitter` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the concept of emitters in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide.
|
||||
* * {@glink framework/deep-dive/event-system Event system} deep dive guide.
|
||||
*
|
||||
* @label NO_ARGUMENTS
|
||||
*/
|
||||
export default function EmitterMixin(): {
|
||||
new (): Emitter;
|
||||
prototype: Emitter;
|
||||
};
|
||||
/**
|
||||
* Emitter/listener interface.
|
||||
*
|
||||
* Can be easily implemented by a class by mixing the {@link module:utils/emittermixin~Emitter} mixin.
|
||||
*
|
||||
* ```ts
|
||||
* class MyClass extends EmitterMixin() {
|
||||
* // This class now implements the `Emitter` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the usage of this interface in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide.
|
||||
* * {@glink framework/deep-dive/event-system Event system} deep-dive guide.
|
||||
*/
|
||||
export interface Emitter {
|
||||
/**
|
||||
* Registers a callback function to be executed when an event is fired.
|
||||
*
|
||||
* Shorthand for {@link #listenTo `this.listenTo( this, event, callback, options )`} (it makes the emitter
|
||||
* listen on itself).
|
||||
*
|
||||
* @typeParam TEvent The type descibing the event. See {@link module:utils/emittermixin~BaseEvent}.
|
||||
* @param event The name of the event.
|
||||
* @param callback The function to be called on event.
|
||||
* @param options Additional options.
|
||||
*/
|
||||
on<TEvent extends BaseEvent>(event: TEvent['name'], callback: GetCallback<TEvent>, options?: GetCallbackOptions<TEvent>): void;
|
||||
/**
|
||||
* Registers a callback function to be executed on the next time the event is fired only. This is similar to
|
||||
* calling {@link #on} followed by {@link #off} in the callback.
|
||||
*
|
||||
* @typeParam TEvent The type descibing the event. See {@link module:utils/emittermixin~BaseEvent}.
|
||||
* @param event The name of the event.
|
||||
* @param callback The function to be called on event.
|
||||
* @param options Additional options.
|
||||
*/
|
||||
once<TEvent extends BaseEvent>(event: TEvent['name'], callback: GetCallback<TEvent>, options?: GetCallbackOptions<TEvent>): void;
|
||||
/**
|
||||
* Stops executing the callback on the given event.
|
||||
* Shorthand for {@link #stopListening `this.stopListening( this, event, callback )`}.
|
||||
*
|
||||
* @param event The name of the event.
|
||||
* @param callback The function to stop being called.
|
||||
*/
|
||||
off(event: string, callback: Function): void;
|
||||
/**
|
||||
* Registers a callback function to be executed when an event is fired in a specific (emitter) object.
|
||||
*
|
||||
* Events can be grouped in namespaces using `:`.
|
||||
* When namespaced event is fired, it additionally fires all callbacks for that namespace.
|
||||
*
|
||||
* ```ts
|
||||
* // myEmitter.on( ... ) is a shorthand for myEmitter.listenTo( myEmitter, ... ).
|
||||
* myEmitter.on( 'myGroup', genericCallback );
|
||||
* myEmitter.on( 'myGroup:myEvent', specificCallback );
|
||||
*
|
||||
* // genericCallback is fired.
|
||||
* myEmitter.fire( 'myGroup' );
|
||||
* // both genericCallback and specificCallback are fired.
|
||||
* myEmitter.fire( 'myGroup:myEvent' );
|
||||
* // genericCallback is fired even though there are no callbacks for "foo".
|
||||
* myEmitter.fire( 'myGroup:foo' );
|
||||
* ```
|
||||
*
|
||||
* An event callback can {@link module:utils/eventinfo~EventInfo#stop stop the event} and
|
||||
* set the {@link module:utils/eventinfo~EventInfo#return return value} of the {@link #fire} method.
|
||||
*
|
||||
* @label BASE_EMITTER
|
||||
* @typeParam TEvent The type describing the event. See {@link module:utils/emittermixin~BaseEvent}.
|
||||
* @param emitter The object that fires the event.
|
||||
* @param event The name of the event.
|
||||
* @param callback The function to be called on event.
|
||||
* @param options Additional options.
|
||||
*/
|
||||
listenTo<TEvent extends BaseEvent>(emitter: Emitter, event: TEvent['name'], callback: GetCallback<TEvent>, options?: GetCallbackOptions<TEvent>): void;
|
||||
/**
|
||||
* Stops listening for events. It can be used at different levels:
|
||||
*
|
||||
* * To stop listening to a specific callback.
|
||||
* * To stop listening to a specific event.
|
||||
* * To stop listening to all events fired by a specific object.
|
||||
* * To stop listening to all events fired by all objects.
|
||||
*
|
||||
* @label BASE_STOP
|
||||
* @param emitter The object to stop listening to. If omitted, stops it for all objects.
|
||||
* @param event (Requires the `emitter`) The name of the event to stop listening to. If omitted, stops it
|
||||
* for all events from `emitter`.
|
||||
* @param callback (Requires the `event`) The function to be removed from the call list for the given
|
||||
* `event`.
|
||||
*/
|
||||
stopListening(emitter?: Emitter, event?: string, callback?: Function): void;
|
||||
/**
|
||||
* Fires an event, executing all callbacks registered for it.
|
||||
*
|
||||
* The first parameter passed to callbacks is an {@link module:utils/eventinfo~EventInfo} object,
|
||||
* followed by the optional `args` provided in the `fire()` method call.
|
||||
*
|
||||
* @typeParam TEvent The type describing the event. See {@link module:utils/emittermixin~BaseEvent}.
|
||||
* @param eventOrInfo The name of the event or `EventInfo` object if event is delegated.
|
||||
* @param args Additional arguments to be passed to the callbacks.
|
||||
* @returns By default the method returns `undefined`. However, the return value can be changed by listeners
|
||||
* through modification of the {@link module:utils/eventinfo~EventInfo#return `evt.return`}'s property (the event info
|
||||
* is the first param of every callback).
|
||||
*/
|
||||
fire<TEvent extends BaseEvent>(eventOrInfo: GetNameOrEventInfo<TEvent>, ...args: TEvent['args']): GetEventInfo<TEvent>['return'];
|
||||
/**
|
||||
* Delegates selected events to another {@link module:utils/emittermixin~Emitter}. For instance:
|
||||
*
|
||||
* ```ts
|
||||
* emitterA.delegate( 'eventX' ).to( emitterB );
|
||||
* emitterA.delegate( 'eventX', 'eventY' ).to( emitterC );
|
||||
* ```
|
||||
*
|
||||
* then `eventX` is delegated (fired by) `emitterB` and `emitterC` along with `data`:
|
||||
*
|
||||
* ```ts
|
||||
* emitterA.fire( 'eventX', data );
|
||||
* ```
|
||||
*
|
||||
* and `eventY` is delegated (fired by) `emitterC` along with `data`:
|
||||
*
|
||||
* ```ts
|
||||
* emitterA.fire( 'eventY', data );
|
||||
* ```
|
||||
*
|
||||
* @param events Event names that will be delegated to another emitter.
|
||||
*/
|
||||
delegate(...events: Array<string>): EmitterMixinDelegateChain;
|
||||
/**
|
||||
* Stops delegating events. It can be used at different levels:
|
||||
*
|
||||
* * To stop delegating all events.
|
||||
* * To stop delegating a specific event to all emitters.
|
||||
* * To stop delegating a specific event to a specific emitter.
|
||||
*
|
||||
* @param event The name of the event to stop delegating. If omitted, stops it all delegations.
|
||||
* @param emitter (requires `event`) The object to stop delegating a particular event to.
|
||||
* If omitted, stops delegation of `event` to all emitters.
|
||||
*/
|
||||
stopDelegating(event?: string, emitter?: Emitter): void;
|
||||
}
|
||||
/**
|
||||
* Default type describing any event.
|
||||
*
|
||||
* Every custom event has to be compatible with `BaseEvent`.
|
||||
*
|
||||
* ```ts
|
||||
* type MyEvent = {
|
||||
* // In `fire<MyEvent>( name )`, `on<MyEvent>( name )`, `once<MyEvent>( name )` and `listenTo<MyEvent>( name )` calls
|
||||
* // the `name` argument will be type-checked to ensure it's `'myEvent'` or have `'myEvent:'` prefix.
|
||||
* // Required.
|
||||
* name: 'myEvent' | `myEvent:${ string }`;
|
||||
*
|
||||
* // In `fire<MyEvent>( name, a, b )` call, `a` and `b` parameters will be type-checked against `number` and `string`.
|
||||
* // In `on<MyEvent>`, `once<MyEvent>` and `listenTo<MyEvent>` calls, the parameters of provided callback function
|
||||
* // will be automatically inferred as `EventInfo`, `number` and `string`.
|
||||
* // Required.
|
||||
* args: [ number, string ];
|
||||
*
|
||||
* // `fire<MyEvent>` will have return type `boolean | undefined`.
|
||||
* // Optional, unknown by default.
|
||||
* return: boolean;
|
||||
*
|
||||
* // `fire<MyEvent>( eventInfo )` will type-check that `eventInfo` is `MyEventInfo`, not a base `EventInfo` or string.
|
||||
* // In `on<MyEvent>`, `once<MyEvent>` and `listenTo<MyEvent>` calls, the first callback parameter will be of this type.
|
||||
* // Optional.
|
||||
* eventInfo: MyEventInfo;
|
||||
*
|
||||
* // In `on<MyEvent>`, `once<MyEvent>` and `listenTo<MyEvent>` calls, the `options` parameter will be of type
|
||||
* // `{ myOption?: boolean; priority?: PriorityString }
|
||||
* // Optional.
|
||||
* callbackOptions: { myOption?: boolean };
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
export type BaseEvent = {
|
||||
name: string;
|
||||
args: Array<any>;
|
||||
};
|
||||
/**
|
||||
* Utility type that gets the `EventInfo` subclass for the given event.
|
||||
*/
|
||||
export type GetEventInfo<TEvent extends BaseEvent> = TEvent extends {
|
||||
eventInfo: EventInfo;
|
||||
} ? TEvent['eventInfo'] : EventInfo<TEvent['name'], (TEvent extends {
|
||||
return: infer TReturn;
|
||||
} ? TReturn : unknown)>;
|
||||
/**
|
||||
* Utility type that gets the `EventInfo` subclass or event name type for the given event.
|
||||
*/
|
||||
export type GetNameOrEventInfo<TEvent extends BaseEvent> = TEvent extends {
|
||||
eventInfo: EventInfo;
|
||||
} ? TEvent['eventInfo'] : TEvent['name'] | EventInfo<TEvent['name'], (TEvent extends {
|
||||
return: infer TReturn;
|
||||
} ? TReturn : unknown)>;
|
||||
/**
|
||||
* Utility type that gets the callback type for the given event.
|
||||
*/
|
||||
export type GetCallback<TEvent extends BaseEvent> = (this: Emitter, ev: GetEventInfo<TEvent>, ...args: TEvent['args']) => void;
|
||||
/**
|
||||
* Utility type that gets the callback options for the given event.
|
||||
*/
|
||||
export type GetCallbackOptions<TEvent extends BaseEvent> = TEvent extends {
|
||||
callbackOptions: infer TOptions;
|
||||
} ? TOptions & CallbackOptions : CallbackOptions;
|
||||
/**
|
||||
* Additional options for registering a callback.
|
||||
*/
|
||||
export interface CallbackOptions {
|
||||
/**
|
||||
* The priority of this event callback. The higher
|
||||
* the priority value the sooner the callback will be fired. Events having the same priority are called in the
|
||||
* order they were added.
|
||||
*
|
||||
* @defaultValue `'normal'`
|
||||
*/
|
||||
readonly priority?: PriorityString;
|
||||
}
|
||||
/**
|
||||
* Checks if `listeningEmitter` listens to an emitter with given `listenedToEmitterId` and if so, returns that emitter.
|
||||
* If not, returns `null`.
|
||||
*
|
||||
* @internal
|
||||
* @param listeningEmitter An emitter that listens.
|
||||
* @param listenedToEmitterId Unique emitter id of emitter listened to.
|
||||
*/
|
||||
export declare function _getEmitterListenedTo(listeningEmitter: Emitter, listenedToEmitterId: string): Emitter | null;
|
||||
/**
|
||||
* Sets emitter's unique id.
|
||||
*
|
||||
* **Note:** `_emitterId` can be set only once.
|
||||
*
|
||||
* @internal
|
||||
* @param emitter An emitter for which id will be set.
|
||||
* @param id Unique id to set. If not passed, random unique id will be set.
|
||||
*/
|
||||
export declare function _setEmitterId(emitter: Emitter, id?: string): void;
|
||||
/**
|
||||
* Returns emitter's unique id.
|
||||
*
|
||||
* @internal
|
||||
* @param emitter An emitter which id will be returned.
|
||||
*/
|
||||
export declare function _getEmitterId(emitter: Emitter): string | undefined;
|
||||
/**
|
||||
* The return value of {@link ~Emitter#delegate}.
|
||||
*/
|
||||
export interface EmitterMixinDelegateChain {
|
||||
/**
|
||||
* Selects destination for {@link module:utils/emittermixin~Emitter#delegate} events.
|
||||
*
|
||||
* @param emitter An `EmitterMixin` instance which is the destination for delegated events.
|
||||
* @param nameOrFunction A custom event name or function which converts the original name string.
|
||||
*/
|
||||
to(emitter: Emitter, nameOrFunction?: string | ((name: string) => string)): void;
|
||||
}
|
||||
453
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/emittermixin.js
generated
vendored
Normal file
453
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/emittermixin.js
generated
vendored
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/**
|
||||
* @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/emittermixin
|
||||
*/
|
||||
import EventInfo from './eventinfo.js';
|
||||
import uid from './uid.js';
|
||||
import priorities from './priorities.js';
|
||||
import insertToPriorityArray from './inserttopriorityarray.js';
|
||||
// To check if component is loaded more than once.
|
||||
import './version.js';
|
||||
import CKEditorError from './ckeditorerror.js';
|
||||
const _listeningTo = Symbol('listeningTo');
|
||||
const _emitterId = Symbol('emitterId');
|
||||
const _delegations = Symbol('delegations');
|
||||
const defaultEmitterClass = EmitterMixin(Object);
|
||||
export default function EmitterMixin(base) {
|
||||
if (!base) {
|
||||
return defaultEmitterClass;
|
||||
}
|
||||
class Mixin extends base {
|
||||
on(event, callback, options) {
|
||||
this.listenTo(this, event, callback, options);
|
||||
}
|
||||
once(event, callback, options) {
|
||||
let wasFired = false;
|
||||
const onceCallback = (event, ...args) => {
|
||||
// Ensure the callback is called only once even if the callback itself leads to re-firing the event
|
||||
// (which would call the callback again).
|
||||
if (!wasFired) {
|
||||
wasFired = true;
|
||||
// Go off() at the first call.
|
||||
event.off();
|
||||
// Go with the original callback.
|
||||
callback.call(this, event, ...args);
|
||||
}
|
||||
};
|
||||
// Make a similar on() call, simply replacing the callback.
|
||||
this.listenTo(this, event, onceCallback, options);
|
||||
}
|
||||
off(event, callback) {
|
||||
this.stopListening(this, event, callback);
|
||||
}
|
||||
listenTo(emitter, event, callback, options = {}) {
|
||||
let emitterInfo, eventCallbacks;
|
||||
// _listeningTo contains a list of emitters that this object is listening to.
|
||||
// This list has the following format:
|
||||
//
|
||||
// _listeningTo: {
|
||||
// emitterId: {
|
||||
// emitter: emitter,
|
||||
// callbacks: {
|
||||
// event1: [ callback1, callback2, ... ]
|
||||
// ....
|
||||
// }
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
if (!this[_listeningTo]) {
|
||||
this[_listeningTo] = {};
|
||||
}
|
||||
const emitters = this[_listeningTo];
|
||||
if (!_getEmitterId(emitter)) {
|
||||
_setEmitterId(emitter);
|
||||
}
|
||||
const emitterId = _getEmitterId(emitter);
|
||||
if (!(emitterInfo = emitters[emitterId])) {
|
||||
emitterInfo = emitters[emitterId] = {
|
||||
emitter,
|
||||
callbacks: {}
|
||||
};
|
||||
}
|
||||
if (!(eventCallbacks = emitterInfo.callbacks[event])) {
|
||||
eventCallbacks = emitterInfo.callbacks[event] = [];
|
||||
}
|
||||
eventCallbacks.push(callback);
|
||||
// Finally register the callback to the event.
|
||||
addEventListener(this, emitter, event, callback, options);
|
||||
}
|
||||
stopListening(emitter, event, callback) {
|
||||
const emitters = this[_listeningTo];
|
||||
let emitterId = emitter && _getEmitterId(emitter);
|
||||
const emitterInfo = (emitters && emitterId) ? emitters[emitterId] : undefined;
|
||||
const eventCallbacks = (emitterInfo && event) ? emitterInfo.callbacks[event] : undefined;
|
||||
// Stop if nothing has been listened.
|
||||
if (!emitters || (emitter && !emitterInfo) || (event && !eventCallbacks)) {
|
||||
return;
|
||||
}
|
||||
// All params provided. off() that single callback.
|
||||
if (callback) {
|
||||
removeEventListener(this, emitter, event, callback);
|
||||
// We must remove callbacks as well in order to prevent memory leaks.
|
||||
// See https://github.com/ckeditor/ckeditor5/pull/8480
|
||||
const index = eventCallbacks.indexOf(callback);
|
||||
if (index !== -1) {
|
||||
if (eventCallbacks.length === 1) {
|
||||
delete emitterInfo.callbacks[event];
|
||||
}
|
||||
else {
|
||||
removeEventListener(this, emitter, event, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only `emitter` and `event` provided. off() all callbacks for that event.
|
||||
else if (eventCallbacks) {
|
||||
while ((callback = eventCallbacks.pop())) {
|
||||
removeEventListener(this, emitter, event, callback);
|
||||
}
|
||||
delete emitterInfo.callbacks[event];
|
||||
}
|
||||
// Only `emitter` provided. off() all events for that emitter.
|
||||
else if (emitterInfo) {
|
||||
for (event in emitterInfo.callbacks) {
|
||||
this.stopListening(emitter, event);
|
||||
}
|
||||
delete emitters[emitterId];
|
||||
}
|
||||
// No params provided. off() all emitters.
|
||||
else {
|
||||
for (emitterId in emitters) {
|
||||
this.stopListening(emitters[emitterId].emitter);
|
||||
}
|
||||
delete this[_listeningTo];
|
||||
}
|
||||
}
|
||||
fire(eventOrInfo, ...args) {
|
||||
try {
|
||||
const eventInfo = eventOrInfo instanceof EventInfo ? eventOrInfo : new EventInfo(this, eventOrInfo);
|
||||
const event = eventInfo.name;
|
||||
let callbacks = getCallbacksForEvent(this, event);
|
||||
// Record that the event passed this emitter on its path.
|
||||
eventInfo.path.push(this);
|
||||
// Handle event listener callbacks first.
|
||||
if (callbacks) {
|
||||
// Arguments passed to each callback.
|
||||
const callbackArgs = [eventInfo, ...args];
|
||||
// Copying callbacks array is the easiest and most secure way of preventing infinite loops, when event callbacks
|
||||
// are added while processing other callbacks. Previous solution involved adding counters (unique ids) but
|
||||
// failed if callbacks were added to the queue before currently processed callback.
|
||||
// If this proves to be too inefficient, another method is to change `.on()` so callbacks are stored if same
|
||||
// event is currently processed. Then, `.fire()` at the end, would have to add all stored events.
|
||||
callbacks = Array.from(callbacks);
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
callbacks[i].callback.apply(this, callbackArgs);
|
||||
// Remove the callback from future requests if off() has been called.
|
||||
if (eventInfo.off.called) {
|
||||
// Remove the called mark for the next calls.
|
||||
delete eventInfo.off.called;
|
||||
this._removeEventListener(event, callbacks[i].callback);
|
||||
}
|
||||
// Do not execute next callbacks if stop() was called.
|
||||
if (eventInfo.stop.called) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delegate event to other emitters if needed.
|
||||
const delegations = this[_delegations];
|
||||
if (delegations) {
|
||||
const destinations = delegations.get(event);
|
||||
const passAllDestinations = delegations.get('*');
|
||||
if (destinations) {
|
||||
fireDelegatedEvents(destinations, eventInfo, args);
|
||||
}
|
||||
if (passAllDestinations) {
|
||||
fireDelegatedEvents(passAllDestinations, eventInfo, args);
|
||||
}
|
||||
}
|
||||
return eventInfo.return;
|
||||
}
|
||||
catch (err) {
|
||||
// @if CK_DEBUG // throw err;
|
||||
/* istanbul ignore next -- @preserve */
|
||||
CKEditorError.rethrowUnexpectedError(err, this);
|
||||
}
|
||||
}
|
||||
delegate(...events) {
|
||||
return {
|
||||
to: (emitter, nameOrFunction) => {
|
||||
if (!this[_delegations]) {
|
||||
this[_delegations] = new Map();
|
||||
}
|
||||
// Originally there was a for..of loop which unfortunately caused an error in Babel that didn't allow
|
||||
// build an application. See: https://github.com/ckeditor/ckeditor5-react/issues/40.
|
||||
events.forEach(eventName => {
|
||||
const destinations = this[_delegations].get(eventName);
|
||||
if (!destinations) {
|
||||
this[_delegations].set(eventName, new Map([[emitter, nameOrFunction]]));
|
||||
}
|
||||
else {
|
||||
destinations.set(emitter, nameOrFunction);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
stopDelegating(event, emitter) {
|
||||
if (!this[_delegations]) {
|
||||
return;
|
||||
}
|
||||
if (!event) {
|
||||
this[_delegations].clear();
|
||||
}
|
||||
else if (!emitter) {
|
||||
this[_delegations].delete(event);
|
||||
}
|
||||
else {
|
||||
const destinations = this[_delegations].get(event);
|
||||
if (destinations) {
|
||||
destinations.delete(emitter);
|
||||
}
|
||||
}
|
||||
}
|
||||
_addEventListener(event, callback, options) {
|
||||
createEventNamespace(this, event);
|
||||
const lists = getCallbacksListsForNamespace(this, event);
|
||||
const priority = priorities.get(options.priority);
|
||||
const callbackDefinition = {
|
||||
callback,
|
||||
priority
|
||||
};
|
||||
// Add the callback to all callbacks list.
|
||||
for (const callbacks of lists) {
|
||||
// Add the callback to the list in the right priority position.
|
||||
insertToPriorityArray(callbacks, callbackDefinition);
|
||||
}
|
||||
}
|
||||
_removeEventListener(event, callback) {
|
||||
const lists = getCallbacksListsForNamespace(this, event);
|
||||
for (const callbacks of lists) {
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
if (callbacks[i].callback == callback) {
|
||||
// Remove the callback from the list (fixing the next index).
|
||||
callbacks.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Mixin;
|
||||
}
|
||||
// Backward compatibility with `mix`
|
||||
([
|
||||
'on', 'once', 'off', 'listenTo',
|
||||
'stopListening', 'fire', 'delegate', 'stopDelegating',
|
||||
'_addEventListener', '_removeEventListener'
|
||||
]).forEach(key => {
|
||||
EmitterMixin[key] = defaultEmitterClass.prototype[key];
|
||||
});
|
||||
/**
|
||||
* Checks if `listeningEmitter` listens to an emitter with given `listenedToEmitterId` and if so, returns that emitter.
|
||||
* If not, returns `null`.
|
||||
*
|
||||
* @internal
|
||||
* @param listeningEmitter An emitter that listens.
|
||||
* @param listenedToEmitterId Unique emitter id of emitter listened to.
|
||||
*/
|
||||
export function _getEmitterListenedTo(listeningEmitter, listenedToEmitterId) {
|
||||
const listeningTo = listeningEmitter[_listeningTo];
|
||||
if (listeningTo && listeningTo[listenedToEmitterId]) {
|
||||
return listeningTo[listenedToEmitterId].emitter;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Sets emitter's unique id.
|
||||
*
|
||||
* **Note:** `_emitterId` can be set only once.
|
||||
*
|
||||
* @internal
|
||||
* @param emitter An emitter for which id will be set.
|
||||
* @param id Unique id to set. If not passed, random unique id will be set.
|
||||
*/
|
||||
export function _setEmitterId(emitter, id) {
|
||||
if (!emitter[_emitterId]) {
|
||||
emitter[_emitterId] = id || uid();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns emitter's unique id.
|
||||
*
|
||||
* @internal
|
||||
* @param emitter An emitter which id will be returned.
|
||||
*/
|
||||
export function _getEmitterId(emitter) {
|
||||
return emitter[_emitterId];
|
||||
}
|
||||
/**
|
||||
* Gets the internal `_events` property of the given object.
|
||||
* `_events` property store all lists with callbacks for registered event names.
|
||||
* If there were no events registered on the object, empty `_events` object is created.
|
||||
*/
|
||||
function getEvents(source) {
|
||||
if (!source._events) {
|
||||
Object.defineProperty(source, '_events', {
|
||||
value: {}
|
||||
});
|
||||
}
|
||||
return source._events;
|
||||
}
|
||||
/**
|
||||
* Creates event node for generic-specific events relation architecture.
|
||||
*/
|
||||
function makeEventNode() {
|
||||
return {
|
||||
callbacks: [],
|
||||
childEvents: []
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Creates an architecture for generic-specific events relation.
|
||||
* If needed, creates all events for given eventName, i.e. if the first registered event
|
||||
* is foo:bar:abc, it will create foo:bar:abc, foo:bar and foo event and tie them together.
|
||||
* It also copies callbacks from more generic events to more specific events when
|
||||
* specific events are created.
|
||||
*/
|
||||
function createEventNamespace(source, eventName) {
|
||||
const events = getEvents(source);
|
||||
// First, check if the event we want to add to the structure already exists.
|
||||
if (events[eventName]) {
|
||||
// If it exists, we don't have to do anything.
|
||||
return;
|
||||
}
|
||||
// In other case, we have to create the structure for the event.
|
||||
// Note, that we might need to create intermediate events too.
|
||||
// I.e. if foo:bar:abc is being registered and we only have foo in the structure,
|
||||
// we need to also register foo:bar.
|
||||
// Currently processed event name.
|
||||
let name = eventName;
|
||||
// Name of the event that is a child event for currently processed event.
|
||||
let childEventName = null;
|
||||
// Array containing all newly created specific events.
|
||||
const newEventNodes = [];
|
||||
// While loop can't check for ':' index because we have to handle generic events too.
|
||||
// In each loop, we truncate event name, going from the most specific name to the generic one.
|
||||
// I.e. foo:bar:abc -> foo:bar -> foo.
|
||||
while (name !== '') {
|
||||
if (events[name]) {
|
||||
// If the currently processed event name is already registered, we can be sure
|
||||
// that it already has all the structure created, so we can break the loop here
|
||||
// as no more events need to be registered.
|
||||
break;
|
||||
}
|
||||
// If this event is not yet registered, create a new object for it.
|
||||
events[name] = makeEventNode();
|
||||
// Add it to the array with newly created events.
|
||||
newEventNodes.push(events[name]);
|
||||
// Add previously processed event name as a child of this event.
|
||||
if (childEventName) {
|
||||
events[name].childEvents.push(childEventName);
|
||||
}
|
||||
childEventName = name;
|
||||
// If `.lastIndexOf()` returns -1, `.substr()` will return '' which will break the loop.
|
||||
name = name.substr(0, name.lastIndexOf(':'));
|
||||
}
|
||||
if (name !== '') {
|
||||
// If name is not empty, we found an already registered event that was a parent of the
|
||||
// event we wanted to register.
|
||||
// Copy that event's callbacks to newly registered events.
|
||||
for (const node of newEventNodes) {
|
||||
node.callbacks = events[name].callbacks.slice();
|
||||
}
|
||||
// Add last newly created event to the already registered event.
|
||||
events[name].childEvents.push(childEventName);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets an array containing callbacks list for a given event and it's more specific events.
|
||||
* I.e. if given event is foo:bar and there is also foo:bar:abc event registered, this will
|
||||
* return callback list of foo:bar and foo:bar:abc (but not foo).
|
||||
*/
|
||||
function getCallbacksListsForNamespace(source, eventName) {
|
||||
const eventNode = getEvents(source)[eventName];
|
||||
if (!eventNode) {
|
||||
return [];
|
||||
}
|
||||
let callbacksLists = [eventNode.callbacks];
|
||||
for (let i = 0; i < eventNode.childEvents.length; i++) {
|
||||
const childCallbacksLists = getCallbacksListsForNamespace(source, eventNode.childEvents[i]);
|
||||
callbacksLists = callbacksLists.concat(childCallbacksLists);
|
||||
}
|
||||
return callbacksLists;
|
||||
}
|
||||
/**
|
||||
* Get the list of callbacks for a given event, but only if there any callbacks have been registered.
|
||||
* If there are no callbacks registered for given event, it checks if this is a specific event and looks
|
||||
* for callbacks for it's more generic version.
|
||||
*/
|
||||
function getCallbacksForEvent(source, eventName) {
|
||||
let event;
|
||||
if (!source._events || !(event = source._events[eventName]) || !event.callbacks.length) {
|
||||
// There are no callbacks registered for specified eventName.
|
||||
// But this could be a specific-type event that is in a namespace.
|
||||
if (eventName.indexOf(':') > -1) {
|
||||
// If the eventName is specific, try to find callback lists for more generic event.
|
||||
return getCallbacksForEvent(source, eventName.substr(0, eventName.lastIndexOf(':')));
|
||||
}
|
||||
else {
|
||||
// If this is a top-level generic event, return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return event.callbacks;
|
||||
}
|
||||
/**
|
||||
* Fires delegated events for given map of destinations.
|
||||
*
|
||||
* @param destinations A map containing `[ {@link module:utils/emittermixin~Emitter}, "event name" ]` pair destinations.
|
||||
* @param eventInfo The original event info object.
|
||||
* @param fireArgs Arguments the original event was fired with.
|
||||
*/
|
||||
function fireDelegatedEvents(destinations, eventInfo, fireArgs) {
|
||||
for (let [emitter, name] of destinations) {
|
||||
if (!name) {
|
||||
name = eventInfo.name;
|
||||
}
|
||||
else if (typeof name == 'function') {
|
||||
name = name(eventInfo.name);
|
||||
}
|
||||
const delegatedInfo = new EventInfo(eventInfo.source, name);
|
||||
delegatedInfo.path = [...eventInfo.path];
|
||||
emitter.fire(delegatedInfo, ...fireArgs);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper for registering event callback on the emitter.
|
||||
*/
|
||||
function addEventListener(listener, emitter, event, callback, options) {
|
||||
if (emitter._addEventListener) {
|
||||
emitter._addEventListener(event, callback, options);
|
||||
}
|
||||
else {
|
||||
// Allow listening on objects that do not implement Emitter interface.
|
||||
// This is needed in some tests that are using mocks instead of the real objects with EmitterMixin mixed.
|
||||
(listener._addEventListener).call(emitter, event, callback, options);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper for removing event callback from the emitter.
|
||||
*/
|
||||
function removeEventListener(listener, emitter, event, callback) {
|
||||
if (emitter._removeEventListener) {
|
||||
emitter._removeEventListener(event, callback);
|
||||
}
|
||||
else {
|
||||
// Allow listening on objects that do not implement Emitter interface.
|
||||
// This is needed in some tests that are using mocks instead of the real objects with EmitterMixin mixed.
|
||||
listener._removeEventListener.call(emitter, event, callback);
|
||||
}
|
||||
}
|
||||
117
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/env.d.ts
generated
vendored
Normal file
117
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/env.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* @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/env
|
||||
*/
|
||||
/**
|
||||
* Safely returns `userAgent` from browser's navigator API in a lower case.
|
||||
* If navigator API is not available it will return an empty string.
|
||||
*/
|
||||
export declare function getUserAgent(): string;
|
||||
/**
|
||||
* A namespace containing environment and browser information.
|
||||
*/
|
||||
export interface EnvType {
|
||||
/**
|
||||
* Indicates that the application is running on Macintosh.
|
||||
*/
|
||||
readonly isMac: boolean;
|
||||
/**
|
||||
* Indicates that the application is running on Windows.
|
||||
*/
|
||||
readonly isWindows: boolean;
|
||||
/**
|
||||
* Indicates that the application is running in Firefox (Gecko).
|
||||
*/
|
||||
readonly isGecko: boolean;
|
||||
/**
|
||||
* Indicates that the application is running in Safari.
|
||||
*/
|
||||
readonly isSafari: boolean;
|
||||
/**
|
||||
* Indicates the the application is running in iOS.
|
||||
*/
|
||||
readonly isiOS: boolean;
|
||||
/**
|
||||
* Indicates that the application is running on Android mobile device.
|
||||
*/
|
||||
readonly isAndroid: boolean;
|
||||
/**
|
||||
* Indicates that the application is running in a browser using the Blink engine.
|
||||
*/
|
||||
readonly isBlink: boolean;
|
||||
/**
|
||||
* Environment features information.
|
||||
*/
|
||||
readonly features: EnvFeaturesType;
|
||||
}
|
||||
export interface EnvFeaturesType {
|
||||
/**
|
||||
* Indicates that the environment supports ES2018 Unicode property escapes — like `\p{P}` or `\p{L}`.
|
||||
* More information about unicode properties might be found
|
||||
* [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
|
||||
*/
|
||||
readonly isRegExpUnicodePropertySupported: boolean;
|
||||
}
|
||||
/**
|
||||
* A namespace containing environment and browser information.
|
||||
*/
|
||||
declare const env: EnvType;
|
||||
export default env;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running on Macintosh.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running on Macintosh or not.
|
||||
*/
|
||||
export declare function isMac(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running on Windows.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running on Windows or not.
|
||||
*/
|
||||
export declare function isWindows(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Firefox (Gecko).
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Firefox or not.
|
||||
*/
|
||||
export declare function isGecko(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Safari.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Safari or not.
|
||||
*/
|
||||
export declare function isSafari(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running in iOS.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running in iOS or not.
|
||||
*/
|
||||
export declare function isiOS(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Android mobile device.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Safari or not.
|
||||
*/
|
||||
export declare function isAndroid(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Blink engine.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Blink engine or not.
|
||||
*/
|
||||
export declare function isBlink(userAgent: string): boolean;
|
||||
/**
|
||||
* Checks if the current environment supports ES2018 Unicode properties like `\p{P}` or `\p{L}`.
|
||||
* More information about unicode properties might be found
|
||||
* [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
|
||||
*/
|
||||
export declare function isRegExpUnicodePropertySupported(): boolean;
|
||||
122
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/env.js
generated
vendored
Normal file
122
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/env.js
generated
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/* globals navigator:false */
|
||||
/**
|
||||
* @module utils/env
|
||||
*/
|
||||
/**
|
||||
* Safely returns `userAgent` from browser's navigator API in a lower case.
|
||||
* If navigator API is not available it will return an empty string.
|
||||
*/
|
||||
export function getUserAgent() {
|
||||
// In some environments navigator API might not be available.
|
||||
try {
|
||||
return navigator.userAgent.toLowerCase();
|
||||
}
|
||||
catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
const userAgent = getUserAgent();
|
||||
/**
|
||||
* A namespace containing environment and browser information.
|
||||
*/
|
||||
const env = {
|
||||
isMac: isMac(userAgent),
|
||||
isWindows: isWindows(userAgent),
|
||||
isGecko: isGecko(userAgent),
|
||||
isSafari: isSafari(userAgent),
|
||||
isiOS: isiOS(userAgent),
|
||||
isAndroid: isAndroid(userAgent),
|
||||
isBlink: isBlink(userAgent),
|
||||
features: {
|
||||
isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
|
||||
}
|
||||
};
|
||||
export default env;
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running on Macintosh.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running on Macintosh or not.
|
||||
*/
|
||||
export function isMac(userAgent) {
|
||||
return userAgent.indexOf('macintosh') > -1;
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running on Windows.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running on Windows or not.
|
||||
*/
|
||||
export function isWindows(userAgent) {
|
||||
return userAgent.indexOf('windows') > -1;
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Firefox (Gecko).
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Firefox or not.
|
||||
*/
|
||||
export function isGecko(userAgent) {
|
||||
return !!userAgent.match(/gecko\/\d+/);
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Safari.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Safari or not.
|
||||
*/
|
||||
export function isSafari(userAgent) {
|
||||
return userAgent.indexOf(' applewebkit/') > -1 && userAgent.indexOf('chrome') === -1;
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is running in iOS.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is running in iOS or not.
|
||||
*/
|
||||
export function isiOS(userAgent) {
|
||||
// "Request mobile site" || "Request desktop site".
|
||||
return !!userAgent.match(/iphone|ipad/i) || (isMac(userAgent) && navigator.maxTouchPoints > 0);
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Android mobile device.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Safari or not.
|
||||
*/
|
||||
export function isAndroid(userAgent) {
|
||||
return userAgent.indexOf('android') > -1;
|
||||
}
|
||||
/**
|
||||
* Checks if User Agent represented by the string is Blink engine.
|
||||
*
|
||||
* @param userAgent **Lowercase** `navigator.userAgent` string.
|
||||
* @returns Whether User Agent is Blink engine or not.
|
||||
*/
|
||||
export function isBlink(userAgent) {
|
||||
// The Edge browser before switching to the Blink engine used to report itself as Chrome (and "Edge/")
|
||||
// but after switching to the Blink it replaced "Edge/" with "Edg/".
|
||||
return userAgent.indexOf('chrome/') > -1 && userAgent.indexOf('edge/') < 0;
|
||||
}
|
||||
/**
|
||||
* Checks if the current environment supports ES2018 Unicode properties like `\p{P}` or `\p{L}`.
|
||||
* More information about unicode properties might be found
|
||||
* [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
|
||||
*/
|
||||
export function isRegExpUnicodePropertySupported() {
|
||||
let isSupported = false;
|
||||
// Feature detection for Unicode properties. Added in ES2018. Currently Firefox does not support it.
|
||||
// See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
|
||||
try {
|
||||
// Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
|
||||
isSupported = 'ć'.search(new RegExp('[\\p{L}]', 'u')) === 0;
|
||||
}
|
||||
catch (error) {
|
||||
// Firefox throws a SyntaxError when the group is unsupported.
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
58
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/eventinfo.d.ts
generated
vendored
Normal file
58
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/eventinfo.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* The event object passed to event callbacks. It is used to provide information about the event as well as a tool to
|
||||
* manipulate it.
|
||||
*/
|
||||
export default class EventInfo<TName extends string = string, TReturn = unknown> {
|
||||
/**
|
||||
* The object that fired the event.
|
||||
*/
|
||||
readonly source: object;
|
||||
/**
|
||||
* The event name.
|
||||
*/
|
||||
readonly name: TName;
|
||||
/**
|
||||
* Path this event has followed. See {@link module:utils/emittermixin~Emitter#delegate}.
|
||||
*/
|
||||
path: Array<object>;
|
||||
/**
|
||||
* Stops the event emitter to call further callbacks for this event interaction.
|
||||
*/
|
||||
readonly stop: {
|
||||
(): void;
|
||||
called?: boolean;
|
||||
};
|
||||
/**
|
||||
* Removes the current callback from future interactions of this event.
|
||||
*/
|
||||
readonly off: {
|
||||
(): void;
|
||||
called?: boolean;
|
||||
};
|
||||
/**
|
||||
* The value which will be returned by {@link module:utils/emittermixin~Emitter#fire}.
|
||||
*
|
||||
* It's `undefined` by default and can be changed by an event listener:
|
||||
*
|
||||
* ```ts
|
||||
* dataController.fire( 'getSelectedContent', ( evt ) => {
|
||||
* // This listener will make `dataController.fire( 'getSelectedContent' )`
|
||||
* // always return an empty DocumentFragment.
|
||||
* evt.return = new DocumentFragment();
|
||||
*
|
||||
* // Make sure no other listeners are executed.
|
||||
* evt.stop();
|
||||
* } );
|
||||
* ```
|
||||
*/
|
||||
return: TReturn | undefined;
|
||||
/**
|
||||
* @param source The emitter.
|
||||
* @param name The event name.
|
||||
*/
|
||||
constructor(source: object, name: TName);
|
||||
}
|
||||
26
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/eventinfo.js
generated
vendored
Normal file
26
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/eventinfo.js
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @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/eventinfo
|
||||
*/
|
||||
import spy from './spy.js';
|
||||
/**
|
||||
* The event object passed to event callbacks. It is used to provide information about the event as well as a tool to
|
||||
* manipulate it.
|
||||
*/
|
||||
export default class EventInfo {
|
||||
/**
|
||||
* @param source The emitter.
|
||||
* @param name The event name.
|
||||
*/
|
||||
constructor(source, name) {
|
||||
this.source = source;
|
||||
this.name = name;
|
||||
this.path = [];
|
||||
// The following methods are defined in the constructor because they must be re-created per instance.
|
||||
this.stop = spy();
|
||||
this.off = spy();
|
||||
}
|
||||
}
|
||||
112
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/fastdiff.d.ts
generated
vendored
Normal file
112
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/fastdiff.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* @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 type { DiffResult } from '@ckeditor/ckeditor5-utils/src/diff.js';
|
||||
import type { Change } from '@ckeditor/ckeditor5-utils/src/difftochanges.js';
|
||||
/**
|
||||
* @module utils/fastdiff
|
||||
*/
|
||||
/**
|
||||
* Finds positions of the first and last change in the given string/array and generates a set of changes:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( '12a', '12xyza' );
|
||||
* // [ { index: 2, type: 'insert', values: [ 'x', 'y', 'z' ] } ]
|
||||
*
|
||||
* fastDiff( '12a', '12aa' );
|
||||
* // [ { index: 3, type: 'insert', values: [ 'a' ] } ]
|
||||
*
|
||||
* fastDiff( '12xyza', '12a' );
|
||||
* // [ { index: 2, type: 'delete', howMany: 3 } ]
|
||||
*
|
||||
* fastDiff( [ '1', '2', 'a', 'a' ], [ '1', '2', 'a' ] );
|
||||
* // [ { index: 3, type: 'delete', howMany: 1 } ]
|
||||
*
|
||||
* fastDiff( [ '1', '2', 'a', 'b', 'c', '3' ], [ '2', 'a', 'b' ] );
|
||||
* // [ { index: 0, type: 'insert', values: [ '2', 'a', 'b' ] }, { index: 3, type: 'delete', howMany: 6 } ]
|
||||
* ```
|
||||
*
|
||||
* Passed arrays can contain any type of data, however to compare them correctly custom comparator function
|
||||
* should be passed as a third parameter:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( [ { value: 1 }, { value: 2 } ], [ { value: 1 }, { value: 3 } ], ( a, b ) => {
|
||||
* return a.value === b.value;
|
||||
* } );
|
||||
* // [ { index: 1, type: 'insert', values: [ { value: 3 } ] }, { index: 2, type: 'delete', howMany: 1 } ]
|
||||
* ```
|
||||
*
|
||||
* The resulted set of changes can be applied to the input in order to transform it into the output, for example:
|
||||
*
|
||||
* ```ts
|
||||
* let input = '12abc3';
|
||||
* const output = '2ab';
|
||||
* const changes = fastDiff( input, output );
|
||||
*
|
||||
* changes.forEach( change => {
|
||||
* if ( change.type == 'insert' ) {
|
||||
* input = input.substring( 0, change.index ) + change.values.join( '' ) + input.substring( change.index );
|
||||
* } else if ( change.type == 'delete' ) {
|
||||
* input = input.substring( 0, change.index ) + input.substring( change.index + change.howMany );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* // input equals output now
|
||||
* ```
|
||||
*
|
||||
* or in case of arrays:
|
||||
*
|
||||
* ```ts
|
||||
* let input = [ '1', '2', 'a', 'b', 'c', '3' ];
|
||||
* const output = [ '2', 'a', 'b' ];
|
||||
* const changes = fastDiff( input, output );
|
||||
*
|
||||
* changes.forEach( change => {
|
||||
* if ( change.type == 'insert' ) {
|
||||
* input = input.slice( 0, change.index ).concat( change.values, input.slice( change.index ) );
|
||||
* } else if ( change.type == 'delete' ) {
|
||||
* input = input.slice( 0, change.index ).concat( input.slice( change.index + change.howMany ) );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* // input equals output now
|
||||
* ```
|
||||
*
|
||||
* By passing `true` as the fourth parameter (`atomicChanges`) the output of this function will become compatible with
|
||||
* the {@link module:utils/diff~diff `diff()`} function:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( '12a', '12xyza', undefined, true );
|
||||
* // [ 'equal', 'equal', 'insert', 'insert', 'insert', 'equal' ]
|
||||
* ```
|
||||
*
|
||||
* The default output format of this function is compatible with the output format of
|
||||
* {@link module:utils/difftochanges~diffToChanges `diffToChanges()`}. The `diffToChanges()` input format is, in turn,
|
||||
* compatible with the output of {@link module:utils/diff~diff `diff()`}:
|
||||
*
|
||||
* ```ts
|
||||
* const a = '1234';
|
||||
* const b = '12xyz34';
|
||||
*
|
||||
* // Both calls will return the same results (grouped changes format).
|
||||
* fastDiff( a, b );
|
||||
* diffToChanges( diff( a, b ) );
|
||||
*
|
||||
* // Again, both calls will return the same results (atomic changes format).
|
||||
* fastDiff( a, b, undefined, true );
|
||||
* diff( a, b );
|
||||
* ```
|
||||
*
|
||||
* @typeParam T The type of array elements.
|
||||
* @typeParam AtomicChanges The type of `atomicChanges` parameter (selects the result type).
|
||||
* @param a Input array or string.
|
||||
* @param b Input array or string.
|
||||
* @param cmp Optional function used to compare array values, by default `===` (strict equal operator) is used.
|
||||
* @param atomicChanges Whether an array of `inset|delete|equal` operations should
|
||||
* be returned instead of changes set. This makes this function compatible with {@link module:utils/diff~diff `diff()`}.
|
||||
* Defaults to `false`.
|
||||
* @returns Array of changes. The elements are either {@link module:utils/diff~DiffResult} or {@link module:utils/difftochanges~Change},
|
||||
* depending on `atomicChanges` parameter.
|
||||
*/
|
||||
export default function fastDiff<T, AtomicChanges extends boolean = false>(a: ArrayLike<T>, b: ArrayLike<T>, cmp?: (a: T, b: T) => boolean, atomicChanges?: AtomicChanges): Array<AtomicChanges extends true ? DiffResult : Change<T>>;
|
||||
248
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/fastdiff.js
generated
vendored
Normal file
248
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/fastdiff.js
generated
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/**
|
||||
* @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/fastdiff
|
||||
*/
|
||||
/**
|
||||
* Finds positions of the first and last change in the given string/array and generates a set of changes:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( '12a', '12xyza' );
|
||||
* // [ { index: 2, type: 'insert', values: [ 'x', 'y', 'z' ] } ]
|
||||
*
|
||||
* fastDiff( '12a', '12aa' );
|
||||
* // [ { index: 3, type: 'insert', values: [ 'a' ] } ]
|
||||
*
|
||||
* fastDiff( '12xyza', '12a' );
|
||||
* // [ { index: 2, type: 'delete', howMany: 3 } ]
|
||||
*
|
||||
* fastDiff( [ '1', '2', 'a', 'a' ], [ '1', '2', 'a' ] );
|
||||
* // [ { index: 3, type: 'delete', howMany: 1 } ]
|
||||
*
|
||||
* fastDiff( [ '1', '2', 'a', 'b', 'c', '3' ], [ '2', 'a', 'b' ] );
|
||||
* // [ { index: 0, type: 'insert', values: [ '2', 'a', 'b' ] }, { index: 3, type: 'delete', howMany: 6 } ]
|
||||
* ```
|
||||
*
|
||||
* Passed arrays can contain any type of data, however to compare them correctly custom comparator function
|
||||
* should be passed as a third parameter:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( [ { value: 1 }, { value: 2 } ], [ { value: 1 }, { value: 3 } ], ( a, b ) => {
|
||||
* return a.value === b.value;
|
||||
* } );
|
||||
* // [ { index: 1, type: 'insert', values: [ { value: 3 } ] }, { index: 2, type: 'delete', howMany: 1 } ]
|
||||
* ```
|
||||
*
|
||||
* The resulted set of changes can be applied to the input in order to transform it into the output, for example:
|
||||
*
|
||||
* ```ts
|
||||
* let input = '12abc3';
|
||||
* const output = '2ab';
|
||||
* const changes = fastDiff( input, output );
|
||||
*
|
||||
* changes.forEach( change => {
|
||||
* if ( change.type == 'insert' ) {
|
||||
* input = input.substring( 0, change.index ) + change.values.join( '' ) + input.substring( change.index );
|
||||
* } else if ( change.type == 'delete' ) {
|
||||
* input = input.substring( 0, change.index ) + input.substring( change.index + change.howMany );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* // input equals output now
|
||||
* ```
|
||||
*
|
||||
* or in case of arrays:
|
||||
*
|
||||
* ```ts
|
||||
* let input = [ '1', '2', 'a', 'b', 'c', '3' ];
|
||||
* const output = [ '2', 'a', 'b' ];
|
||||
* const changes = fastDiff( input, output );
|
||||
*
|
||||
* changes.forEach( change => {
|
||||
* if ( change.type == 'insert' ) {
|
||||
* input = input.slice( 0, change.index ).concat( change.values, input.slice( change.index ) );
|
||||
* } else if ( change.type == 'delete' ) {
|
||||
* input = input.slice( 0, change.index ).concat( input.slice( change.index + change.howMany ) );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* // input equals output now
|
||||
* ```
|
||||
*
|
||||
* By passing `true` as the fourth parameter (`atomicChanges`) the output of this function will become compatible with
|
||||
* the {@link module:utils/diff~diff `diff()`} function:
|
||||
*
|
||||
* ```ts
|
||||
* fastDiff( '12a', '12xyza', undefined, true );
|
||||
* // [ 'equal', 'equal', 'insert', 'insert', 'insert', 'equal' ]
|
||||
* ```
|
||||
*
|
||||
* The default output format of this function is compatible with the output format of
|
||||
* {@link module:utils/difftochanges~diffToChanges `diffToChanges()`}. The `diffToChanges()` input format is, in turn,
|
||||
* compatible with the output of {@link module:utils/diff~diff `diff()`}:
|
||||
*
|
||||
* ```ts
|
||||
* const a = '1234';
|
||||
* const b = '12xyz34';
|
||||
*
|
||||
* // Both calls will return the same results (grouped changes format).
|
||||
* fastDiff( a, b );
|
||||
* diffToChanges( diff( a, b ) );
|
||||
*
|
||||
* // Again, both calls will return the same results (atomic changes format).
|
||||
* fastDiff( a, b, undefined, true );
|
||||
* diff( a, b );
|
||||
* ```
|
||||
*
|
||||
* @typeParam T The type of array elements.
|
||||
* @typeParam AtomicChanges The type of `atomicChanges` parameter (selects the result type).
|
||||
* @param a Input array or string.
|
||||
* @param b Input array or string.
|
||||
* @param cmp Optional function used to compare array values, by default `===` (strict equal operator) is used.
|
||||
* @param atomicChanges Whether an array of `inset|delete|equal` operations should
|
||||
* be returned instead of changes set. This makes this function compatible with {@link module:utils/diff~diff `diff()`}.
|
||||
* Defaults to `false`.
|
||||
* @returns Array of changes. The elements are either {@link module:utils/diff~DiffResult} or {@link module:utils/difftochanges~Change},
|
||||
* depending on `atomicChanges` parameter.
|
||||
*/
|
||||
export default function fastDiff(a, b, cmp, atomicChanges) {
|
||||
// Set the comparator function.
|
||||
cmp = cmp || function (a, b) {
|
||||
return a === b;
|
||||
};
|
||||
// Convert the string (or any array-like object - eg. NodeList) to an array by using the slice() method because,
|
||||
// unlike Array.from(), it returns array of UTF-16 code units instead of the code points of a string.
|
||||
// One code point might be a surrogate pair of two code units. All text offsets are expected to be in code units.
|
||||
// See ckeditor/ckeditor5#3147.
|
||||
//
|
||||
// We need to make sure here that fastDiff() works identical to diff().
|
||||
const arrayA = Array.isArray(a) ? a : Array.prototype.slice.call(a);
|
||||
const arrayB = Array.isArray(b) ? b : Array.prototype.slice.call(b);
|
||||
// Find first and last change.
|
||||
const changeIndexes = findChangeBoundaryIndexes(arrayA, arrayB, cmp);
|
||||
// Transform into changes array.
|
||||
const result = atomicChanges ?
|
||||
changeIndexesToAtomicChanges(changeIndexes, arrayB.length) :
|
||||
changeIndexesToChanges(arrayB, changeIndexes);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Finds position of the first and last change in the given arrays. For example:
|
||||
*
|
||||
* ```ts
|
||||
* const indexes = findChangeBoundaryIndexes( [ '1', '2', '3', '4' ], [ '1', '3', '4', '2', '4' ] );
|
||||
* console.log( indexes ); // { firstIndex: 1, lastIndexOld: 3, lastIndexNew: 4 }
|
||||
* ```
|
||||
*
|
||||
* The above indexes means that in the first array the modified part is `1[23]4` and in the second array it is `1[342]4`.
|
||||
* Based on such indexes, array with `insert`/`delete` operations which allows transforming first value into the second one
|
||||
* can be generated.
|
||||
*/
|
||||
function findChangeBoundaryIndexes(arr1, arr2, cmp) {
|
||||
// Find the first difference between passed values.
|
||||
const firstIndex = findFirstDifferenceIndex(arr1, arr2, cmp);
|
||||
// If arrays are equal return -1 indexes object.
|
||||
if (firstIndex === -1) {
|
||||
return { firstIndex: -1, lastIndexOld: -1, lastIndexNew: -1 };
|
||||
}
|
||||
// Remove the common part of each value and reverse them to make it simpler to find the last difference between them.
|
||||
const oldArrayReversed = cutAndReverse(arr1, firstIndex);
|
||||
const newArrayReversed = cutAndReverse(arr2, firstIndex);
|
||||
// Find the first difference between reversed values.
|
||||
// It should be treated as "how many elements from the end the last difference occurred".
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// initial -> after cut -> reversed:
|
||||
// oldValue: '321ba' -> '21ba' -> 'ab12'
|
||||
// newValue: '31xba' -> '1xba' -> 'abx1'
|
||||
// lastIndex: -> 2
|
||||
//
|
||||
// So the last change occurred two characters from the end of the arrays.
|
||||
const lastIndex = findFirstDifferenceIndex(oldArrayReversed, newArrayReversed, cmp);
|
||||
// Use `lastIndex` to calculate proper offset, starting from the beginning (`lastIndex` kind of starts from the end).
|
||||
const lastIndexOld = arr1.length - lastIndex;
|
||||
const lastIndexNew = arr2.length - lastIndex;
|
||||
return { firstIndex, lastIndexOld, lastIndexNew };
|
||||
}
|
||||
/**
|
||||
* Returns a first index on which given arrays differ. If both arrays are the same, -1 is returned.
|
||||
*/
|
||||
function findFirstDifferenceIndex(arr1, arr2, cmp) {
|
||||
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
||||
if (arr1[i] === undefined || arr2[i] === undefined || !cmp(arr1[i], arr2[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1; // Return -1 if arrays are equal.
|
||||
}
|
||||
/**
|
||||
* Returns a copy of the given array with `howMany` elements removed starting from the beginning and in reversed order.
|
||||
*
|
||||
* @param arr Array to be processed.
|
||||
* @param howMany How many elements from array beginning to remove.
|
||||
* @returns Shortened and reversed array.
|
||||
*/
|
||||
function cutAndReverse(arr, howMany) {
|
||||
return arr.slice(howMany).reverse();
|
||||
}
|
||||
/**
|
||||
* Generates changes array based on change indexes from `findChangeBoundaryIndexes` function. This function will
|
||||
* generate array with 0 (no changes), 1 (deletion or insertion) or 2 records (insertion and deletion).
|
||||
*
|
||||
* @param newArray New array for which change indexes were calculated.
|
||||
* @param changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
|
||||
* @returns Array of changes compatible with {@link module:utils/difftochanges~diffToChanges} format.
|
||||
*/
|
||||
function changeIndexesToChanges(newArray, changeIndexes) {
|
||||
const result = [];
|
||||
const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
|
||||
// Order operations as 'insert', 'delete' array to keep compatibility with {@link module:utils/difftochanges~diffToChanges}
|
||||
// in most cases. However, 'diffToChanges' does not stick to any order so in some cases
|
||||
// (for example replacing '12345' with 'abcd') it will generate 'delete', 'insert' order.
|
||||
if (lastIndexNew - firstIndex > 0) {
|
||||
result.push({
|
||||
index: firstIndex,
|
||||
type: 'insert',
|
||||
values: newArray.slice(firstIndex, lastIndexNew)
|
||||
});
|
||||
}
|
||||
if (lastIndexOld - firstIndex > 0) {
|
||||
result.push({
|
||||
index: firstIndex + (lastIndexNew - firstIndex),
|
||||
type: 'delete',
|
||||
howMany: lastIndexOld - firstIndex
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Generates array with set `equal|insert|delete` operations based on change indexes from `findChangeBoundaryIndexes` function.
|
||||
*
|
||||
* @param changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
|
||||
* @param newLength Length of the new array on which `findChangeBoundaryIndexes` calculated change indexes.
|
||||
* @returns Array of changes compatible with {@link module:utils/diff~diff} format.
|
||||
*/
|
||||
function changeIndexesToAtomicChanges(changeIndexes, newLength) {
|
||||
const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
|
||||
// No changes.
|
||||
if (firstIndex === -1) {
|
||||
return Array(newLength).fill('equal');
|
||||
}
|
||||
let result = [];
|
||||
if (firstIndex > 0) {
|
||||
result = result.concat(Array(firstIndex).fill('equal'));
|
||||
}
|
||||
if (lastIndexNew - firstIndex > 0) {
|
||||
result = result.concat(Array(lastIndexNew - firstIndex).fill('insert'));
|
||||
}
|
||||
if (lastIndexOld - firstIndex > 0) {
|
||||
result = result.concat(Array(lastIndexOld - firstIndex).fill('delete'));
|
||||
}
|
||||
if (lastIndexNew < newLength) {
|
||||
result = result.concat(Array(newLength - lastIndexNew).fill('equal'));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/first.d.ts
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/first.d.ts
generated
vendored
Normal file
|
|
@ -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/first
|
||||
*/
|
||||
/**
|
||||
* Returns first item of the given `iterator`.
|
||||
*/
|
||||
export default function first<T>(iterator: Iterator<T>): T | null;
|
||||
17
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/first.js
generated
vendored
Normal file
17
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/first.js
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* @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/first
|
||||
*/
|
||||
/**
|
||||
* Returns first item of the given `iterator`.
|
||||
*/
|
||||
export default function first(iterator) {
|
||||
const iteratorItem = iterator.next();
|
||||
if (iteratorItem.done) {
|
||||
return null;
|
||||
}
|
||||
return iteratorItem.value;
|
||||
}
|
||||
75
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/focustracker.d.ts
generated
vendored
Normal file
75
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/focustracker.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
declare const FocusTracker_base: import("@ckeditor/ckeditor5-utils/src/mix.js").Mixed<{
|
||||
new (): import("@ckeditor/ckeditor5-utils/src/observablemixin.js").Observable;
|
||||
prototype: import("@ckeditor/ckeditor5-utils/src/observablemixin.js").Observable;
|
||||
}, import("@ckeditor/ckeditor5-utils/src/dom/emittermixin.js").DomEmitter>;
|
||||
/**
|
||||
* Allows observing a group of `Element`s whether at least one of them is focused.
|
||||
*
|
||||
* Used by the {@link module:core/editor/editor~Editor} in order to track whether the focus is still within the application,
|
||||
* or were used outside of its UI.
|
||||
*
|
||||
* **Note** `focus` and `blur` listeners use event capturing, so it is only needed to register wrapper `Element`
|
||||
* which contain other `focusable` elements. But note that this wrapper element has to be focusable too
|
||||
* (have e.g. `tabindex="-1"`).
|
||||
*
|
||||
* Check out the {@glink framework/deep-dive/ui/focus-tracking "Deep dive into focus tracking"} guide to learn more.
|
||||
*/
|
||||
export default class FocusTracker extends FocusTracker_base {
|
||||
/**
|
||||
* True when one of the registered elements is focused.
|
||||
*
|
||||
* @readonly
|
||||
* @observable
|
||||
*/
|
||||
isFocused: boolean;
|
||||
/**
|
||||
* The currently focused element.
|
||||
*
|
||||
* While {@link #isFocused `isFocused`} remains `true`, the focus can
|
||||
* move between different UI elements. This property tracks those
|
||||
* elements and tells which one is currently focused.
|
||||
*
|
||||
* @readonly
|
||||
* @observable
|
||||
*/
|
||||
focusedElement: Element | null;
|
||||
/**
|
||||
* List of registered elements.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_elements: Set<Element>;
|
||||
/**
|
||||
* Event loop timeout.
|
||||
*/
|
||||
private _nextEventLoopTimeout;
|
||||
constructor();
|
||||
/**
|
||||
* Starts tracking the specified element.
|
||||
*/
|
||||
add(element: Element): void;
|
||||
/**
|
||||
* Stops tracking the specified element and stops listening on this element.
|
||||
*/
|
||||
remove(element: Element): void;
|
||||
/**
|
||||
* Destroys the focus tracker by:
|
||||
* - Disabling all event listeners attached to tracked elements.
|
||||
* - Removing all tracked elements that were previously added.
|
||||
*/
|
||||
destroy(): void;
|
||||
/**
|
||||
* Stores currently focused element and set {@link #isFocused} as `true`.
|
||||
*/
|
||||
private _focus;
|
||||
/**
|
||||
* Clears currently focused element and set {@link #isFocused} as `false`.
|
||||
* This method uses `setTimeout` to change order of fires `blur` and `focus` events.
|
||||
*/
|
||||
private _blur;
|
||||
}
|
||||
export {};
|
||||
95
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/focustracker.js
generated
vendored
Normal file
95
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/focustracker.js
generated
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/* global setTimeout, clearTimeout */
|
||||
/**
|
||||
* @module utils/focustracker
|
||||
*/
|
||||
import DomEmitterMixin from './dom/emittermixin.js';
|
||||
import ObservableMixin from './observablemixin.js';
|
||||
import CKEditorError from './ckeditorerror.js';
|
||||
/**
|
||||
* Allows observing a group of `Element`s whether at least one of them is focused.
|
||||
*
|
||||
* Used by the {@link module:core/editor/editor~Editor} in order to track whether the focus is still within the application,
|
||||
* or were used outside of its UI.
|
||||
*
|
||||
* **Note** `focus` and `blur` listeners use event capturing, so it is only needed to register wrapper `Element`
|
||||
* which contain other `focusable` elements. But note that this wrapper element has to be focusable too
|
||||
* (have e.g. `tabindex="-1"`).
|
||||
*
|
||||
* Check out the {@glink framework/deep-dive/ui/focus-tracking "Deep dive into focus tracking"} guide to learn more.
|
||||
*/
|
||||
export default class FocusTracker extends DomEmitterMixin(ObservableMixin()) {
|
||||
constructor() {
|
||||
super();
|
||||
/**
|
||||
* List of registered elements.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
this._elements = new Set();
|
||||
/**
|
||||
* Event loop timeout.
|
||||
*/
|
||||
this._nextEventLoopTimeout = null;
|
||||
this.set('isFocused', false);
|
||||
this.set('focusedElement', null);
|
||||
}
|
||||
/**
|
||||
* Starts tracking the specified element.
|
||||
*/
|
||||
add(element) {
|
||||
if (this._elements.has(element)) {
|
||||
/**
|
||||
* This element is already tracked by {@link module:utils/focustracker~FocusTracker}.
|
||||
*
|
||||
* @error focustracker-add-element-already-exist
|
||||
*/
|
||||
throw new CKEditorError('focustracker-add-element-already-exist', this);
|
||||
}
|
||||
this.listenTo(element, 'focus', () => this._focus(element), { useCapture: true });
|
||||
this.listenTo(element, 'blur', () => this._blur(), { useCapture: true });
|
||||
this._elements.add(element);
|
||||
}
|
||||
/**
|
||||
* Stops tracking the specified element and stops listening on this element.
|
||||
*/
|
||||
remove(element) {
|
||||
if (element === this.focusedElement) {
|
||||
this._blur();
|
||||
}
|
||||
if (this._elements.has(element)) {
|
||||
this.stopListening(element);
|
||||
this._elements.delete(element);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Destroys the focus tracker by:
|
||||
* - Disabling all event listeners attached to tracked elements.
|
||||
* - Removing all tracked elements that were previously added.
|
||||
*/
|
||||
destroy() {
|
||||
this.stopListening();
|
||||
}
|
||||
/**
|
||||
* Stores currently focused element and set {@link #isFocused} as `true`.
|
||||
*/
|
||||
_focus(element) {
|
||||
clearTimeout(this._nextEventLoopTimeout);
|
||||
this.focusedElement = element;
|
||||
this.isFocused = true;
|
||||
}
|
||||
/**
|
||||
* Clears currently focused element and set {@link #isFocused} as `false`.
|
||||
* This method uses `setTimeout` to change order of fires `blur` and `focus` events.
|
||||
*/
|
||||
_blur() {
|
||||
clearTimeout(this._nextEventLoopTimeout);
|
||||
this._nextEventLoopTimeout = setTimeout(() => {
|
||||
this.focusedElement = null;
|
||||
this.isFocused = false;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
64
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/index.d.ts
generated
vendored
Normal file
64
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
export { default as env } from '@ckeditor/ckeditor5-utils/src/env.js';
|
||||
export { default as diff, type DiffResult } from '@ckeditor/ckeditor5-utils/src/diff.js';
|
||||
export { default as fastDiff } from '@ckeditor/ckeditor5-utils/src/fastdiff.js';
|
||||
export { default as diffToChanges } from '@ckeditor/ckeditor5-utils/src/difftochanges.js';
|
||||
export { default as mix } from '@ckeditor/ckeditor5-utils/src/mix.js';
|
||||
export type { Constructor, Mixed } from '@ckeditor/ckeditor5-utils/src/mix.js';
|
||||
export { default as EmitterMixin, type Emitter, type BaseEvent, type CallbackOptions, type EmitterMixinDelegateChain, type GetCallback, type GetCallbackOptions, type GetEventInfo, type GetNameOrEventInfo } from '@ckeditor/ckeditor5-utils/src/emittermixin.js';
|
||||
export { default as EventInfo } from '@ckeditor/ckeditor5-utils/src/eventinfo.js';
|
||||
export { default as ObservableMixin, type Observable, type DecoratedMethodEvent, type ObservableChangeEvent, type ObservableSetEvent } from '@ckeditor/ckeditor5-utils/src/observablemixin.js';
|
||||
export { default as CKEditorError, logError, logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror.js';
|
||||
export { default as ElementReplacer } from '@ckeditor/ckeditor5-utils/src/elementreplacer.js';
|
||||
export { default as abortableDebounce, type AbortableFunc } from '@ckeditor/ckeditor5-utils/src/abortabledebounce.js';
|
||||
export { default as count } from '@ckeditor/ckeditor5-utils/src/count.js';
|
||||
export { default as compareArrays } from '@ckeditor/ckeditor5-utils/src/comparearrays.js';
|
||||
export { default as createElement } from '@ckeditor/ckeditor5-utils/src/dom/createelement.js';
|
||||
export { default as Config } from '@ckeditor/ckeditor5-utils/src/config.js';
|
||||
export { default as isIterable } from '@ckeditor/ckeditor5-utils/src/isiterable.js';
|
||||
export { default as DomEmitterMixin, type DomEmitter } from '@ckeditor/ckeditor5-utils/src/dom/emittermixin.js';
|
||||
export { default as findClosestScrollableAncestor } from '@ckeditor/ckeditor5-utils/src/dom/findclosestscrollableancestor.js';
|
||||
export { default as global } from '@ckeditor/ckeditor5-utils/src/dom/global.js';
|
||||
export { default as getAncestors } from '@ckeditor/ckeditor5-utils/src/dom/getancestors.js';
|
||||
export { default as getDataFromElement } from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement.js';
|
||||
export { default as getBorderWidths } from '@ckeditor/ckeditor5-utils/src/dom/getborderwidths.js';
|
||||
export { default as isText } from '@ckeditor/ckeditor5-utils/src/dom/istext.js';
|
||||
export { default as Rect, type RectSource } from '@ckeditor/ckeditor5-utils/src/dom/rect.js';
|
||||
export { default as ResizeObserver } from '@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js';
|
||||
export { default as setDataInElement } from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement.js';
|
||||
export { default as toUnit } from '@ckeditor/ckeditor5-utils/src/dom/tounit.js';
|
||||
export { default as indexOf } from '@ckeditor/ckeditor5-utils/src/dom/indexof.js';
|
||||
export { default as insertAt } from '@ckeditor/ckeditor5-utils/src/dom/insertat.js';
|
||||
export { default as isComment } from '@ckeditor/ckeditor5-utils/src/dom/iscomment.js';
|
||||
export { default as isNode } from '@ckeditor/ckeditor5-utils/src/dom/isnode.js';
|
||||
export { default as isRange } from '@ckeditor/ckeditor5-utils/src/dom/isrange.js';
|
||||
export { default as isValidAttributeName } from '@ckeditor/ckeditor5-utils/src/dom/isvalidattributename.js';
|
||||
export { default as isVisible } from '@ckeditor/ckeditor5-utils/src/dom/isvisible.js';
|
||||
export { getOptimalPosition, type Options as PositionOptions, type PositioningFunction, type DomPoint } from '@ckeditor/ckeditor5-utils/src/dom/position.js';
|
||||
export { default as remove } from '@ckeditor/ckeditor5-utils/src/dom/remove.js';
|
||||
export * from '@ckeditor/ckeditor5-utils/src/dom/scroll.js';
|
||||
export * from '@ckeditor/ckeditor5-utils/src/keyboard.js';
|
||||
export * from '@ckeditor/ckeditor5-utils/src/language.js';
|
||||
export { default as Locale, type LocaleTranslate, type Translations } from '@ckeditor/ckeditor5-utils/src/locale.js';
|
||||
export { default as Collection, type CollectionAddEvent, type CollectionChangeEvent, type CollectionRemoveEvent } from '@ckeditor/ckeditor5-utils/src/collection.js';
|
||||
export { default as first } from '@ckeditor/ckeditor5-utils/src/first.js';
|
||||
export { default as FocusTracker } from '@ckeditor/ckeditor5-utils/src/focustracker.js';
|
||||
export { default as KeystrokeHandler } from '@ckeditor/ckeditor5-utils/src/keystrokehandler.js';
|
||||
export { default as toArray, type ArrayOrItem, type ReadonlyArrayOrItem } from '@ckeditor/ckeditor5-utils/src/toarray.js';
|
||||
export { default as toMap } from '@ckeditor/ckeditor5-utils/src/tomap.js';
|
||||
export { default as priorities, type PriorityString } from '@ckeditor/ckeditor5-utils/src/priorities.js';
|
||||
export { default as retry, exponentialDelay } from '@ckeditor/ckeditor5-utils/src/retry.js';
|
||||
export { default as insertToPriorityArray } from '@ckeditor/ckeditor5-utils/src/inserttopriorityarray.js';
|
||||
export { default as spliceArray } from '@ckeditor/ckeditor5-utils/src/splicearray.js';
|
||||
export { default as uid } from '@ckeditor/ckeditor5-utils/src/uid.js';
|
||||
export { default as delay, type DelayedFunc } from '@ckeditor/ckeditor5-utils/src/delay.js';
|
||||
export { default as verifyLicense } from '@ckeditor/ckeditor5-utils/src/verifylicense.js';
|
||||
export { default as wait } from '@ckeditor/ckeditor5-utils/src/wait.js';
|
||||
export * from '@ckeditor/ckeditor5-utils/src/unicode.js';
|
||||
export { default as version, releaseDate } from '@ckeditor/ckeditor5-utils/src/version.js';
|
||||
63
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/index.js
generated
vendored
Normal file
63
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
export { default as env } from './env.js';
|
||||
export { default as diff } from './diff.js';
|
||||
export { default as fastDiff } from './fastdiff.js';
|
||||
export { default as diffToChanges } from './difftochanges.js';
|
||||
export { default as mix } from './mix.js';
|
||||
export { default as EmitterMixin } from './emittermixin.js';
|
||||
export { default as EventInfo } from './eventinfo.js';
|
||||
export { default as ObservableMixin } from './observablemixin.js';
|
||||
export { default as CKEditorError, logError, logWarning } from './ckeditorerror.js';
|
||||
export { default as ElementReplacer } from './elementreplacer.js';
|
||||
export { default as abortableDebounce } from './abortabledebounce.js';
|
||||
export { default as count } from './count.js';
|
||||
export { default as compareArrays } from './comparearrays.js';
|
||||
export { default as createElement } from './dom/createelement.js';
|
||||
export { default as Config } from './config.js';
|
||||
export { default as isIterable } from './isiterable.js';
|
||||
export { default as DomEmitterMixin } from './dom/emittermixin.js';
|
||||
export { default as findClosestScrollableAncestor } from './dom/findclosestscrollableancestor.js';
|
||||
export { default as global } from './dom/global.js';
|
||||
export { default as getAncestors } from './dom/getancestors.js';
|
||||
export { default as getDataFromElement } from './dom/getdatafromelement.js';
|
||||
export { default as getBorderWidths } from './dom/getborderwidths.js';
|
||||
export { default as isText } from './dom/istext.js';
|
||||
export { default as Rect } from './dom/rect.js';
|
||||
export { default as ResizeObserver } from './dom/resizeobserver.js';
|
||||
export { default as setDataInElement } from './dom/setdatainelement.js';
|
||||
export { default as toUnit } from './dom/tounit.js';
|
||||
export { default as indexOf } from './dom/indexof.js';
|
||||
export { default as insertAt } from './dom/insertat.js';
|
||||
export { default as isComment } from './dom/iscomment.js';
|
||||
export { default as isNode } from './dom/isnode.js';
|
||||
export { default as isRange } from './dom/isrange.js';
|
||||
export { default as isValidAttributeName } from './dom/isvalidattributename.js';
|
||||
export { default as isVisible } from './dom/isvisible.js';
|
||||
export { getOptimalPosition } from './dom/position.js';
|
||||
export { default as remove } from './dom/remove.js';
|
||||
export * from './dom/scroll.js';
|
||||
export * from './keyboard.js';
|
||||
export * from './language.js';
|
||||
export { default as Locale } from './locale.js';
|
||||
export { default as Collection } from './collection.js';
|
||||
export { default as first } from './first.js';
|
||||
export { default as FocusTracker } from './focustracker.js';
|
||||
export { default as KeystrokeHandler } from './keystrokehandler.js';
|
||||
export { default as toArray } from './toarray.js';
|
||||
export { default as toMap } from './tomap.js';
|
||||
export { default as priorities } from './priorities.js';
|
||||
export { default as retry, exponentialDelay } from './retry.js';
|
||||
export { default as insertToPriorityArray } from './inserttopriorityarray.js';
|
||||
export { default as spliceArray } from './splicearray.js';
|
||||
export { default as uid } from './uid.js';
|
||||
export { default as delay } from './delay.js';
|
||||
export { default as verifyLicense } from './verifylicense.js';
|
||||
export { default as wait } from './wait.js';
|
||||
export * from './unicode.js';
|
||||
export { default as version, releaseDate } from './version.js';
|
||||
30
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/inserttopriorityarray.d.ts
generated
vendored
Normal file
30
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/inserttopriorityarray.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @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 { type PriorityString } from '@ckeditor/ckeditor5-utils/src/priorities.js';
|
||||
/**
|
||||
* @module utils/inserttopriorityarray
|
||||
*/
|
||||
/**
|
||||
* The priority object descriptor.
|
||||
*
|
||||
* ```ts
|
||||
* const objectWithPriority = {
|
||||
* priority: 'high'
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface ObjectWithPriority {
|
||||
/**
|
||||
* Priority of the object.
|
||||
*/
|
||||
priority: PriorityString;
|
||||
}
|
||||
/**
|
||||
* Inserts any object with priority at correct index by priority so registered objects are always sorted from highest to lowest priority.
|
||||
*
|
||||
* @param objects Array of objects with priority to insert object to.
|
||||
* @param objectToInsert Object with `priority` property.
|
||||
*/
|
||||
export default function insertToPriorityArray<T extends ObjectWithPriority>(objects: Array<T>, objectToInsert: T): void;
|
||||
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/inserttopriorityarray.js
generated
vendored
Normal file
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/inserttopriorityarray.js
generated
vendored
Normal file
|
|
@ -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
|
||||
*/
|
||||
import priorities from './priorities.js';
|
||||
/**
|
||||
* Inserts any object with priority at correct index by priority so registered objects are always sorted from highest to lowest priority.
|
||||
*
|
||||
* @param objects Array of objects with priority to insert object to.
|
||||
* @param objectToInsert Object with `priority` property.
|
||||
*/
|
||||
export default function insertToPriorityArray(objects, objectToInsert) {
|
||||
const priority = priorities.get(objectToInsert.priority);
|
||||
for (let i = 0; i < objects.length; i++) {
|
||||
if (priorities.get(objects[i].priority) < priority) {
|
||||
objects.splice(i, 0, objectToInsert);
|
||||
return;
|
||||
}
|
||||
}
|
||||
objects.push(objectToInsert);
|
||||
}
|
||||
14
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/isiterable.d.ts
generated
vendored
Normal file
14
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/isiterable.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* @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/isiterable
|
||||
*/
|
||||
/**
|
||||
* Checks if value implements iterator interface.
|
||||
*
|
||||
* @param value The value to check.
|
||||
* @returns True if value implements iterator interface.
|
||||
*/
|
||||
export default function isIterable(value: any): value is Iterable<any>;
|
||||
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/isiterable.js
generated
vendored
Normal file
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/isiterable.js
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @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/isiterable
|
||||
*/
|
||||
/**
|
||||
* Checks if value implements iterator interface.
|
||||
*
|
||||
* @param value The value to check.
|
||||
* @returns True if value implements iterator interface.
|
||||
*/
|
||||
export default function isIterable(value) {
|
||||
return !!(value && value[Symbol.iterator]);
|
||||
}
|
||||
126
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keyboard.d.ts
generated
vendored
Normal file
126
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keyboard.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/**
|
||||
* @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 set of utilities related to keyboard support.
|
||||
*
|
||||
* @module utils/keyboard
|
||||
*/
|
||||
import type { LanguageDirection } from '@ckeditor/ckeditor5-utils/src/language.js';
|
||||
/**
|
||||
* An object with `keyName => keyCode` pairs for a set of known keys.
|
||||
*
|
||||
* Contains:
|
||||
*
|
||||
* * `a-z`,
|
||||
* * `0-9`,
|
||||
* * `f1-f12`,
|
||||
* * `` ` ``, `-`, `=`, `[`, `]`, `;`, `'`, `,`, `.`, `/`, `\`,
|
||||
* * `arrow(left|up|right|bottom)`,
|
||||
* * `backspace`, `delete`, `enter`, `esc`, `tab`,
|
||||
* * `ctrl`, `cmd`, `shift`, `alt`.
|
||||
*/
|
||||
export declare const keyCodes: {
|
||||
readonly [keyCode: string]: number;
|
||||
};
|
||||
/**
|
||||
* Converts a key name or {@link module:utils/keyboard~KeystrokeInfo keystroke info} into a key code.
|
||||
*
|
||||
* Note: Key names are matched with {@link module:utils/keyboard#keyCodes} in a case-insensitive way.
|
||||
*
|
||||
* @param key A key name (see {@link module:utils/keyboard#keyCodes}) or a keystroke data object.
|
||||
* @returns Key or keystroke code.
|
||||
*/
|
||||
export declare function getCode(key: string | Readonly<KeystrokeInfo>): number;
|
||||
/**
|
||||
* Parses the keystroke and returns a keystroke code that will match the code returned by
|
||||
* {@link module:utils/keyboard~getCode} for the corresponding {@link module:utils/keyboard~KeystrokeInfo keystroke info}.
|
||||
*
|
||||
* The keystroke can be passed in two formats:
|
||||
*
|
||||
* * as a single string – e.g. `ctrl + A`,
|
||||
* * as an array of {@link module:utils/keyboard~keyCodes known key names} and key codes – e.g.:
|
||||
* * `[ 'ctrl', 32 ]` (ctrl + space),
|
||||
* * `[ 'ctrl', 'a' ]` (ctrl + A).
|
||||
*
|
||||
* Note: Key names are matched with {@link module:utils/keyboard#keyCodes} in a case-insensitive way.
|
||||
*
|
||||
* Note: Only keystrokes with a single non-modifier key are supported (e.g. `ctrl+A` is OK, but `ctrl+A+B` is not).
|
||||
*
|
||||
* Note: On macOS, keystroke handling is translating the `Ctrl` key to the `Cmd` key and handling only that keystroke.
|
||||
* For example, a registered keystroke `Ctrl+A` will be translated to `Cmd+A` on macOS. To disable the translation of some keystroke,
|
||||
* use the forced modifier: `Ctrl!+A` (note the exclamation mark).
|
||||
*
|
||||
* @param keystroke The keystroke definition.
|
||||
* @returns Keystroke code.
|
||||
*/
|
||||
export declare function parseKeystroke(keystroke: string | ReadonlyArray<number | string>): number;
|
||||
/**
|
||||
* Translates any keystroke string text like `"Ctrl+A"` to an
|
||||
* environment–specific keystroke, i.e. `"⌘A"` on macOS.
|
||||
*
|
||||
* @param keystroke The keystroke text.
|
||||
* @returns The keystroke text specific for the environment.
|
||||
*/
|
||||
export declare function getEnvKeystrokeText(keystroke: string): string;
|
||||
/**
|
||||
* Returns `true` if the provided key code represents one of the arrow keys.
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
*/
|
||||
export declare function isArrowKeyCode(keyCode: number): boolean;
|
||||
/**
|
||||
* String representing a direction of an arrow key kode.
|
||||
*/
|
||||
export type ArrowKeyCodeDirection = 'left' | 'up' | 'right' | 'down';
|
||||
/**
|
||||
* Returns the direction in which the {@link module:engine/model/documentselection~DocumentSelection selection}
|
||||
* will move when the provided arrow key code is pressed considering the language direction of the editor content.
|
||||
*
|
||||
* For instance, in right–to–left (RTL) content languages, pressing the left arrow means moving the selection right (forward)
|
||||
* in the model structure. Similarly, pressing the right arrow moves the selection left (backward).
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
* @param contentLanguageDirection The content language direction, corresponding to
|
||||
* {@link module:utils/locale~Locale#contentLanguageDirection}.
|
||||
* @returns Localized arrow direction or `undefined` for non-arrow key codes.
|
||||
*/
|
||||
export declare function getLocalizedArrowKeyCodeDirection(keyCode: number, contentLanguageDirection: LanguageDirection): ArrowKeyCodeDirection | undefined;
|
||||
/**
|
||||
* Determines if the provided key code moves the {@link module:engine/model/documentselection~DocumentSelection selection}
|
||||
* forward or backward considering the language direction of the editor content.
|
||||
*
|
||||
* For instance, in right–to–left (RTL) languages, pressing the left arrow means moving forward
|
||||
* in the model structure. Similarly, pressing the right arrow moves the selection backward.
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
* @param contentLanguageDirection The content language direction, corresponding to
|
||||
* {@link module:utils/locale~Locale#contentLanguageDirection}.
|
||||
*/
|
||||
export declare function isForwardArrowKeyCode(keyCode: number, contentLanguageDirection: LanguageDirection): boolean;
|
||||
/**
|
||||
* Information about the keystroke.
|
||||
*/
|
||||
export interface KeystrokeInfo {
|
||||
/**
|
||||
* The [key code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode).
|
||||
*/
|
||||
keyCode: number;
|
||||
/**
|
||||
* Whether the <kbd>Alt</kbd> modifier was pressed.
|
||||
*/
|
||||
altKey: boolean;
|
||||
/**
|
||||
* Whether the <kbd>Cmd</kbd> modifier was pressed.
|
||||
*/
|
||||
metaKey: boolean;
|
||||
/**
|
||||
* Whether the <kbd>Ctrl</kbd> modifier was pressed.
|
||||
*/
|
||||
ctrlKey: boolean;
|
||||
/**
|
||||
* Whether the <kbd>Shift</kbd> modifier was pressed.
|
||||
*/
|
||||
shiftKey: boolean;
|
||||
}
|
||||
251
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keyboard.js
generated
vendored
Normal file
251
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keyboard.js
generated
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/**
|
||||
* @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 CKEditorError from './ckeditorerror.js';
|
||||
import env from './env.js';
|
||||
const modifiersToGlyphsMac = {
|
||||
ctrl: '⌃',
|
||||
cmd: '⌘',
|
||||
alt: '⌥',
|
||||
shift: '⇧'
|
||||
};
|
||||
const modifiersToGlyphsNonMac = {
|
||||
ctrl: 'Ctrl+',
|
||||
alt: 'Alt+',
|
||||
shift: 'Shift+'
|
||||
};
|
||||
const keyCodesToGlyphs = {
|
||||
37: '←',
|
||||
38: '↑',
|
||||
39: '→',
|
||||
40: '↓',
|
||||
9: '⇥',
|
||||
33: 'Page Up',
|
||||
34: 'Page Down'
|
||||
};
|
||||
/**
|
||||
* An object with `keyName => keyCode` pairs for a set of known keys.
|
||||
*
|
||||
* Contains:
|
||||
*
|
||||
* * `a-z`,
|
||||
* * `0-9`,
|
||||
* * `f1-f12`,
|
||||
* * `` ` ``, `-`, `=`, `[`, `]`, `;`, `'`, `,`, `.`, `/`, `\`,
|
||||
* * `arrow(left|up|right|bottom)`,
|
||||
* * `backspace`, `delete`, `enter`, `esc`, `tab`,
|
||||
* * `ctrl`, `cmd`, `shift`, `alt`.
|
||||
*/
|
||||
export const keyCodes = generateKnownKeyCodes();
|
||||
const keyCodeNames = Object.fromEntries(Object.entries(keyCodes).map(([name, code]) => {
|
||||
let prettyKeyName;
|
||||
if (code in keyCodesToGlyphs) {
|
||||
prettyKeyName = keyCodesToGlyphs[code];
|
||||
}
|
||||
else {
|
||||
prettyKeyName = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
}
|
||||
return [code, prettyKeyName];
|
||||
}));
|
||||
/**
|
||||
* Converts a key name or {@link module:utils/keyboard~KeystrokeInfo keystroke info} into a key code.
|
||||
*
|
||||
* Note: Key names are matched with {@link module:utils/keyboard#keyCodes} in a case-insensitive way.
|
||||
*
|
||||
* @param key A key name (see {@link module:utils/keyboard#keyCodes}) or a keystroke data object.
|
||||
* @returns Key or keystroke code.
|
||||
*/
|
||||
export function getCode(key) {
|
||||
let keyCode;
|
||||
if (typeof key == 'string') {
|
||||
keyCode = keyCodes[key.toLowerCase()];
|
||||
if (!keyCode) {
|
||||
/**
|
||||
* Unknown key name. Only key names included in the {@link module:utils/keyboard#keyCodes} can be used.
|
||||
*
|
||||
* @error keyboard-unknown-key
|
||||
* @param {String} key
|
||||
*/
|
||||
throw new CKEditorError('keyboard-unknown-key', null, { key });
|
||||
}
|
||||
}
|
||||
else {
|
||||
keyCode = key.keyCode +
|
||||
(key.altKey ? keyCodes.alt : 0) +
|
||||
(key.ctrlKey ? keyCodes.ctrl : 0) +
|
||||
(key.shiftKey ? keyCodes.shift : 0) +
|
||||
(key.metaKey ? keyCodes.cmd : 0);
|
||||
}
|
||||
return keyCode;
|
||||
}
|
||||
/**
|
||||
* Parses the keystroke and returns a keystroke code that will match the code returned by
|
||||
* {@link module:utils/keyboard~getCode} for the corresponding {@link module:utils/keyboard~KeystrokeInfo keystroke info}.
|
||||
*
|
||||
* The keystroke can be passed in two formats:
|
||||
*
|
||||
* * as a single string – e.g. `ctrl + A`,
|
||||
* * as an array of {@link module:utils/keyboard~keyCodes known key names} and key codes – e.g.:
|
||||
* * `[ 'ctrl', 32 ]` (ctrl + space),
|
||||
* * `[ 'ctrl', 'a' ]` (ctrl + A).
|
||||
*
|
||||
* Note: Key names are matched with {@link module:utils/keyboard#keyCodes} in a case-insensitive way.
|
||||
*
|
||||
* Note: Only keystrokes with a single non-modifier key are supported (e.g. `ctrl+A` is OK, but `ctrl+A+B` is not).
|
||||
*
|
||||
* Note: On macOS, keystroke handling is translating the `Ctrl` key to the `Cmd` key and handling only that keystroke.
|
||||
* For example, a registered keystroke `Ctrl+A` will be translated to `Cmd+A` on macOS. To disable the translation of some keystroke,
|
||||
* use the forced modifier: `Ctrl!+A` (note the exclamation mark).
|
||||
*
|
||||
* @param keystroke The keystroke definition.
|
||||
* @returns Keystroke code.
|
||||
*/
|
||||
export function parseKeystroke(keystroke) {
|
||||
if (typeof keystroke == 'string') {
|
||||
keystroke = splitKeystrokeText(keystroke);
|
||||
}
|
||||
return keystroke
|
||||
.map(key => (typeof key == 'string') ? getEnvKeyCode(key) : key)
|
||||
.reduce((key, sum) => sum + key, 0);
|
||||
}
|
||||
/**
|
||||
* Translates any keystroke string text like `"Ctrl+A"` to an
|
||||
* environment–specific keystroke, i.e. `"⌘A"` on macOS.
|
||||
*
|
||||
* @param keystroke The keystroke text.
|
||||
* @returns The keystroke text specific for the environment.
|
||||
*/
|
||||
export function getEnvKeystrokeText(keystroke) {
|
||||
let keystrokeCode = parseKeystroke(keystroke);
|
||||
const modifiersToGlyphs = Object.entries((env.isMac || env.isiOS) ? modifiersToGlyphsMac : modifiersToGlyphsNonMac);
|
||||
const modifiers = modifiersToGlyphs.reduce((modifiers, [name, glyph]) => {
|
||||
// Modifier keys are stored as a bit mask so extract those from the keystroke code.
|
||||
if ((keystrokeCode & keyCodes[name]) != 0) {
|
||||
keystrokeCode &= ~keyCodes[name];
|
||||
modifiers += glyph;
|
||||
}
|
||||
return modifiers;
|
||||
}, '');
|
||||
return modifiers + (keystrokeCode ? keyCodeNames[keystrokeCode] : '');
|
||||
}
|
||||
/**
|
||||
* Returns `true` if the provided key code represents one of the arrow keys.
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
*/
|
||||
export function isArrowKeyCode(keyCode) {
|
||||
return keyCode == keyCodes.arrowright ||
|
||||
keyCode == keyCodes.arrowleft ||
|
||||
keyCode == keyCodes.arrowup ||
|
||||
keyCode == keyCodes.arrowdown;
|
||||
}
|
||||
/**
|
||||
* Returns the direction in which the {@link module:engine/model/documentselection~DocumentSelection selection}
|
||||
* will move when the provided arrow key code is pressed considering the language direction of the editor content.
|
||||
*
|
||||
* For instance, in right–to–left (RTL) content languages, pressing the left arrow means moving the selection right (forward)
|
||||
* in the model structure. Similarly, pressing the right arrow moves the selection left (backward).
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
* @param contentLanguageDirection The content language direction, corresponding to
|
||||
* {@link module:utils/locale~Locale#contentLanguageDirection}.
|
||||
* @returns Localized arrow direction or `undefined` for non-arrow key codes.
|
||||
*/
|
||||
export function getLocalizedArrowKeyCodeDirection(keyCode, contentLanguageDirection) {
|
||||
const isLtrContent = contentLanguageDirection === 'ltr';
|
||||
switch (keyCode) {
|
||||
case keyCodes.arrowleft:
|
||||
return isLtrContent ? 'left' : 'right';
|
||||
case keyCodes.arrowright:
|
||||
return isLtrContent ? 'right' : 'left';
|
||||
case keyCodes.arrowup:
|
||||
return 'up';
|
||||
case keyCodes.arrowdown:
|
||||
return 'down';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Converts a key name to the key code with mapping based on the env.
|
||||
*
|
||||
* See: {@link module:utils/keyboard~getCode}.
|
||||
*
|
||||
* @param key The key name (see {@link module:utils/keyboard#keyCodes}).
|
||||
* @returns Key code.
|
||||
*/
|
||||
function getEnvKeyCode(key) {
|
||||
// Don't remap modifier key for forced modifiers.
|
||||
if (key.endsWith('!')) {
|
||||
return getCode(key.slice(0, -1));
|
||||
}
|
||||
const code = getCode(key);
|
||||
return (env.isMac || env.isiOS) && code == keyCodes.ctrl ? keyCodes.cmd : code;
|
||||
}
|
||||
/**
|
||||
* Determines if the provided key code moves the {@link module:engine/model/documentselection~DocumentSelection selection}
|
||||
* forward or backward considering the language direction of the editor content.
|
||||
*
|
||||
* For instance, in right–to–left (RTL) languages, pressing the left arrow means moving forward
|
||||
* in the model structure. Similarly, pressing the right arrow moves the selection backward.
|
||||
*
|
||||
* @param keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
|
||||
* @param contentLanguageDirection The content language direction, corresponding to
|
||||
* {@link module:utils/locale~Locale#contentLanguageDirection}.
|
||||
*/
|
||||
export function isForwardArrowKeyCode(keyCode, contentLanguageDirection) {
|
||||
const localizedKeyCodeDirection = getLocalizedArrowKeyCodeDirection(keyCode, contentLanguageDirection);
|
||||
return localizedKeyCodeDirection === 'down' || localizedKeyCodeDirection === 'right';
|
||||
}
|
||||
function generateKnownKeyCodes() {
|
||||
const keyCodes = {
|
||||
pageup: 33,
|
||||
pagedown: 34,
|
||||
arrowleft: 37,
|
||||
arrowup: 38,
|
||||
arrowright: 39,
|
||||
arrowdown: 40,
|
||||
backspace: 8,
|
||||
delete: 46,
|
||||
enter: 13,
|
||||
space: 32,
|
||||
esc: 27,
|
||||
tab: 9,
|
||||
// The idea about these numbers is that they do not collide with any real key codes, so we can use them
|
||||
// like bit masks.
|
||||
ctrl: 0x110000,
|
||||
shift: 0x220000,
|
||||
alt: 0x440000,
|
||||
cmd: 0x880000
|
||||
};
|
||||
// a-z
|
||||
for (let code = 65; code <= 90; code++) {
|
||||
const letter = String.fromCharCode(code);
|
||||
keyCodes[letter.toLowerCase()] = code;
|
||||
}
|
||||
// 0-9
|
||||
for (let code = 48; code <= 57; code++) {
|
||||
keyCodes[code - 48] = code;
|
||||
}
|
||||
// F1-F12
|
||||
for (let code = 112; code <= 123; code++) {
|
||||
keyCodes['f' + (code - 111)] = code;
|
||||
}
|
||||
// other characters
|
||||
Object.assign(keyCodes, {
|
||||
'\'': 222,
|
||||
',': 108,
|
||||
'-': 109,
|
||||
'.': 110,
|
||||
'/': 111,
|
||||
';': 186,
|
||||
'=': 187,
|
||||
'[': 219,
|
||||
'\\': 220,
|
||||
']': 221,
|
||||
'`': 223
|
||||
});
|
||||
return keyCodes;
|
||||
}
|
||||
function splitKeystrokeText(keystroke) {
|
||||
return keystroke.split('+').map(key => key.trim());
|
||||
}
|
||||
87
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keystrokehandler.d.ts
generated
vendored
Normal file
87
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keystrokehandler.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @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 type { Emitter } from '@ckeditor/ckeditor5-utils/src/emittermixin.js';
|
||||
import { type KeystrokeInfo } from '@ckeditor/ckeditor5-utils/src/keyboard.js';
|
||||
import type { PriorityString } from '@ckeditor/ckeditor5-utils/src/priorities.js';
|
||||
/**
|
||||
* Keystroke handler allows registering callbacks for given keystrokes.
|
||||
*
|
||||
* The most frequent use of this class is through the {@link module:core/editor/editor~Editor#keystrokes `editor.keystrokes`}
|
||||
* property. It allows listening to keystrokes executed in the editing view:
|
||||
*
|
||||
* ```ts
|
||||
* editor.keystrokes.set( 'Ctrl+A', ( keyEvtData, cancel ) => {
|
||||
* console.log( 'Ctrl+A has been pressed' );
|
||||
* cancel();
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* However, this utility class can be used in various part of the UI. For instance, a certain {@link module:ui/view~View}
|
||||
* can use it like this:
|
||||
*
|
||||
* ```ts
|
||||
* class MyView extends View {
|
||||
* constructor() {
|
||||
* this.keystrokes = new KeystrokeHandler();
|
||||
*
|
||||
* this.keystrokes.set( 'tab', handleTabKey );
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* super.render();
|
||||
*
|
||||
* this.keystrokes.listenTo( this.element );
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* That keystroke handler will listen to `keydown` events fired in this view's main element.
|
||||
*
|
||||
*/
|
||||
export default class KeystrokeHandler {
|
||||
/**
|
||||
* Listener used to listen to events for easier keystroke handler destruction.
|
||||
*/
|
||||
private readonly _listener;
|
||||
/**
|
||||
* Creates an instance of the keystroke handler.
|
||||
*/
|
||||
constructor();
|
||||
/**
|
||||
* Starts listening for `keydown` events from a given emitter.
|
||||
*/
|
||||
listenTo(emitter: Emitter | HTMLElement | Window): void;
|
||||
/**
|
||||
* Registers a handler for the specified keystroke.
|
||||
*
|
||||
* @param keystroke Keystroke defined in a format accepted by
|
||||
* the {@link module:utils/keyboard~parseKeystroke} function.
|
||||
* @param callback A function called with the
|
||||
* {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
|
||||
* a helper function to call both `preventDefault()` and `stopPropagation()` on the underlying event.
|
||||
* @param options Additional options.
|
||||
* @param options.priority The priority of the keystroke
|
||||
* callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
|
||||
* are called in the order they were added.
|
||||
*/
|
||||
set(keystroke: string | ReadonlyArray<string | number>, callback: (ev: KeyboardEvent, cancel: () => void) => void, options?: {
|
||||
readonly priority?: PriorityString;
|
||||
}): void;
|
||||
/**
|
||||
* Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
|
||||
*
|
||||
* @param keyEvtData Key event data.
|
||||
* @returns Whether the keystroke was handled.
|
||||
*/
|
||||
press(keyEvtData: Readonly<KeystrokeInfo>): boolean;
|
||||
/**
|
||||
* Stops listening to `keydown` events from the given emitter.
|
||||
*/
|
||||
stopListening(emitter?: Emitter | HTMLElement | Window): void;
|
||||
/**
|
||||
* Destroys the keystroke handler.
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
122
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keystrokehandler.js
generated
vendored
Normal file
122
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/keystrokehandler.js
generated
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* @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/keystrokehandler
|
||||
*/
|
||||
import DomEmitterMixin from './dom/emittermixin.js';
|
||||
import { getCode, parseKeystroke } from './keyboard.js';
|
||||
/**
|
||||
* Keystroke handler allows registering callbacks for given keystrokes.
|
||||
*
|
||||
* The most frequent use of this class is through the {@link module:core/editor/editor~Editor#keystrokes `editor.keystrokes`}
|
||||
* property. It allows listening to keystrokes executed in the editing view:
|
||||
*
|
||||
* ```ts
|
||||
* editor.keystrokes.set( 'Ctrl+A', ( keyEvtData, cancel ) => {
|
||||
* console.log( 'Ctrl+A has been pressed' );
|
||||
* cancel();
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* However, this utility class can be used in various part of the UI. For instance, a certain {@link module:ui/view~View}
|
||||
* can use it like this:
|
||||
*
|
||||
* ```ts
|
||||
* class MyView extends View {
|
||||
* constructor() {
|
||||
* this.keystrokes = new KeystrokeHandler();
|
||||
*
|
||||
* this.keystrokes.set( 'tab', handleTabKey );
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* super.render();
|
||||
*
|
||||
* this.keystrokes.listenTo( this.element );
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* That keystroke handler will listen to `keydown` events fired in this view's main element.
|
||||
*
|
||||
*/
|
||||
export default class KeystrokeHandler {
|
||||
/**
|
||||
* Creates an instance of the keystroke handler.
|
||||
*/
|
||||
constructor() {
|
||||
this._listener = new (DomEmitterMixin())();
|
||||
}
|
||||
/**
|
||||
* Starts listening for `keydown` events from a given emitter.
|
||||
*/
|
||||
listenTo(emitter) {
|
||||
// The #_listener works here as a kind of dispatcher. It groups the events coming from the same
|
||||
// keystroke so the listeners can be attached to them with different priorities.
|
||||
//
|
||||
// E.g. all the keystrokes with the `keyCode` of 42 coming from the `emitter` are propagated
|
||||
// as a `_keydown:42` event by the `_listener`. If there's a callback created by the `set`
|
||||
// method for this 42 keystroke, it listens to the `_listener#_keydown:42` event only and interacts
|
||||
// only with other listeners of this particular event, thus making it possible to prioritize
|
||||
// the listeners and safely cancel execution, when needed. Instead of duplicating the Emitter logic,
|
||||
// the KeystrokeHandler re–uses it to do its job.
|
||||
this._listener.listenTo(emitter, 'keydown', (evt, keyEvtData) => {
|
||||
this._listener.fire('_keydown:' + getCode(keyEvtData), keyEvtData);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Registers a handler for the specified keystroke.
|
||||
*
|
||||
* @param keystroke Keystroke defined in a format accepted by
|
||||
* the {@link module:utils/keyboard~parseKeystroke} function.
|
||||
* @param callback A function called with the
|
||||
* {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
|
||||
* a helper function to call both `preventDefault()` and `stopPropagation()` on the underlying event.
|
||||
* @param options Additional options.
|
||||
* @param options.priority The priority of the keystroke
|
||||
* callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
|
||||
* are called in the order they were added.
|
||||
*/
|
||||
set(keystroke, callback, options = {}) {
|
||||
const keyCode = parseKeystroke(keystroke);
|
||||
const priority = options.priority;
|
||||
// Execute the passed callback on KeystrokeHandler#_keydown.
|
||||
// TODO: https://github.com/ckeditor/ckeditor5-utils/issues/144
|
||||
this._listener.listenTo(this._listener, '_keydown:' + keyCode, (evt, keyEvtData) => {
|
||||
callback(keyEvtData, () => {
|
||||
// Stop the event in the DOM: no listener in the web page
|
||||
// will be triggered by this event.
|
||||
keyEvtData.preventDefault();
|
||||
keyEvtData.stopPropagation();
|
||||
// Stop the event in the KeystrokeHandler: no more callbacks
|
||||
// will be executed for this keystroke.
|
||||
evt.stop();
|
||||
});
|
||||
// Mark this keystroke as handled by the callback. See: #press.
|
||||
evt.return = true;
|
||||
}, { priority });
|
||||
}
|
||||
/**
|
||||
* Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
|
||||
*
|
||||
* @param keyEvtData Key event data.
|
||||
* @returns Whether the keystroke was handled.
|
||||
*/
|
||||
press(keyEvtData) {
|
||||
return !!this._listener.fire('_keydown:' + getCode(keyEvtData), keyEvtData);
|
||||
}
|
||||
/**
|
||||
* Stops listening to `keydown` events from the given emitter.
|
||||
*/
|
||||
stopListening(emitter) {
|
||||
this._listener.stopListening(emitter);
|
||||
}
|
||||
/**
|
||||
* Destroys the keystroke handler.
|
||||
*/
|
||||
destroy() {
|
||||
this.stopListening();
|
||||
}
|
||||
}
|
||||
17
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/language.d.ts
generated
vendored
Normal file
17
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/language.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* @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/language
|
||||
*/
|
||||
/**
|
||||
* String representing a language direction.
|
||||
*/
|
||||
export type LanguageDirection = 'ltr' | 'rtl';
|
||||
/**
|
||||
* Helps determine whether a language text direction is LTR or RTL.
|
||||
*
|
||||
* @param languageCode The ISO 639-1 or ISO 639-2 language code.
|
||||
*/
|
||||
export declare function getLanguageDirection(languageCode: string): LanguageDirection;
|
||||
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/language.js
generated
vendored
Normal file
20
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/language.js
generated
vendored
Normal file
|
|
@ -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
|
||||
*/
|
||||
const RTL_LANGUAGE_CODES = [
|
||||
'ar', 'ara',
|
||||
'dv', 'div',
|
||||
'fa', 'per', 'fas',
|
||||
'he', 'heb',
|
||||
'ku', 'kur',
|
||||
'ug', 'uig' // Uighur, Uyghur
|
||||
];
|
||||
/**
|
||||
* Helps determine whether a language text direction is LTR or RTL.
|
||||
*
|
||||
* @param languageCode The ISO 639-1 or ISO 639-2 language code.
|
||||
*/
|
||||
export function getLanguageDirection(languageCode) {
|
||||
return RTL_LANGUAGE_CODES.includes(languageCode) ? 'rtl' : 'ltr';
|
||||
}
|
||||
141
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/locale.d.ts
generated
vendored
Normal file
141
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/locale.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* @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/locale
|
||||
*/
|
||||
import { type ArrayOrItem } from '@ckeditor/ckeditor5-utils/src/toarray.js';
|
||||
import { type Message } from '@ckeditor/ckeditor5-utils/src/translation-service.js';
|
||||
import { type LanguageDirection } from '@ckeditor/ckeditor5-utils/src/language.js';
|
||||
/**
|
||||
* Represents the localization services.
|
||||
*/
|
||||
export default class Locale {
|
||||
/**
|
||||
* The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
|
||||
*
|
||||
* If the {@link #contentLanguage content language} was not specified in the `Locale` constructor,
|
||||
* it also defines the language of the content.
|
||||
*/
|
||||
readonly uiLanguage: string;
|
||||
/**
|
||||
* Text direction of the {@link #uiLanguage editor UI language}. Either `'ltr'` or `'rtl'`.
|
||||
*/
|
||||
readonly uiLanguageDirection: LanguageDirection;
|
||||
/**
|
||||
* The editor content language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
|
||||
*
|
||||
* Usually the same as the {@link #uiLanguage editor language}, it can be customized by passing an optional
|
||||
* argument to the `Locale` constructor.
|
||||
*/
|
||||
readonly contentLanguage: string;
|
||||
/**
|
||||
* Text direction of the {@link #contentLanguage editor content language}.
|
||||
*
|
||||
* If the content language was passed directly to the `Locale` constructor, this property represents the
|
||||
* direction of that language.
|
||||
*
|
||||
* If the {@link #contentLanguage editor content language} was derived from the {@link #uiLanguage editor language},
|
||||
* the content language direction is the same as the {@link #uiLanguageDirection UI language direction}.
|
||||
*
|
||||
* The value is either `'ltr'` or `'rtl'`.
|
||||
*/
|
||||
readonly contentLanguageDirection: LanguageDirection;
|
||||
/**
|
||||
* Translates the given message to the {@link #uiLanguage}. This method is also available in
|
||||
* {@link module:core/editor/editor~Editor#t `Editor`} and {@link module:ui/view~View#t `View`}.
|
||||
*
|
||||
* This method's context is statically bound to the `Locale` instance and **should always be called as a function**:
|
||||
*
|
||||
* ```ts
|
||||
* const t = locale.t;
|
||||
* t( 'Label' );
|
||||
* ```
|
||||
*
|
||||
* The message can be either a string or an object implementing the {@link module:utils/translation-service~Message} interface.
|
||||
*
|
||||
* The message may contain placeholders (`%<index>`) for value(s) that are passed as a `values` parameter.
|
||||
* For an array of values, the `%<index>` will be changed to an element of that array at the given index.
|
||||
* For a single value passed as the second argument, only the `%0` placeholders will be changed to the provided value.
|
||||
*
|
||||
* ```ts
|
||||
* t( 'Created file "%0" in %1ms.', [ fileName, timeTaken ] );
|
||||
* t( 'Created file "%0", fileName );
|
||||
* ```
|
||||
*
|
||||
* The message supports plural forms. To specify the plural form, use the `plural` property. Singular or plural form
|
||||
* will be chosen depending on the first value from the passed `values`. The value of the `plural` property is used
|
||||
* as a default plural translation when the translation for the target language is missing.
|
||||
*
|
||||
* ```ts
|
||||
* t( { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Add a space' for the English language.
|
||||
* t( { string: 'Add a space', plural: 'Add %0 spaces' }, 5 ); // 'Add 5 spaces' for the English language.
|
||||
* t( { string: '%1 a space', plural: '%1 %0 spaces' }, [ 2, 'Add' ] ); // 'Add 2 spaces' for the English language.
|
||||
*
|
||||
* t( { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Dodaj spację' for the Polish language.
|
||||
* t( { string: 'Add a space', plural: 'Add %0 spaces' }, 5 ); // 'Dodaj 5 spacji' for the Polish language.
|
||||
* t( { string: '%1 a space', plural: '%1 %0 spaces' }, [ 2, 'Add' ] ); // 'Dodaj 2 spacje' for the Polish language.
|
||||
* ```
|
||||
*
|
||||
* * The message should provide an ID using the `id` property when the message strings are not unique and their
|
||||
* translations should be different.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'en', { string: 'image', id: 'ADD_IMAGE' } );
|
||||
* translate( 'en', { string: 'image', id: 'AN_IMAGE' } );
|
||||
* ```
|
||||
*/
|
||||
readonly t: LocaleTranslate;
|
||||
/**
|
||||
* Object that contains translations.
|
||||
*/
|
||||
translations?: Translations;
|
||||
/**
|
||||
* Creates a new instance of the locale class. Learn more about
|
||||
* {@glink features/ui-language configuring the language of the editor}.
|
||||
*
|
||||
* @param options Locale configuration.
|
||||
* @param options.uiLanguage The editor UI language code in the
|
||||
* [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. See {@link #uiLanguage}.
|
||||
* @param options.contentLanguage The editor content language code in the
|
||||
* [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. If not specified, the same as `options.language`.
|
||||
* See {@link #contentLanguage}.
|
||||
* @param translations Translations passed as a editor config parameter.
|
||||
*/
|
||||
constructor({ uiLanguage, contentLanguage, translations }?: {
|
||||
readonly uiLanguage?: string;
|
||||
readonly contentLanguage?: string;
|
||||
readonly translations?: ArrayOrItem<Translations>;
|
||||
});
|
||||
/**
|
||||
* The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
|
||||
*
|
||||
* **Note**: This property was deprecated. Please use {@link #uiLanguage} and {@link #contentLanguage}
|
||||
* properties instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get language(): string;
|
||||
/**
|
||||
* An unbound version of the {@link #t} method.
|
||||
*/
|
||||
private _t;
|
||||
}
|
||||
/**
|
||||
* @param message A message that will be localized (translated).
|
||||
* @param values A value or an array of values that will fill message placeholders.
|
||||
* For messages supporting plural forms the first value will determine the plural form.
|
||||
*/
|
||||
export type LocaleTranslate = (message: string | Message, values?: number | string | ReadonlyArray<number | string>) => string;
|
||||
/**
|
||||
* Translations object definition.
|
||||
*/
|
||||
export type Translations = {
|
||||
[language: string]: {
|
||||
dictionary: {
|
||||
[messageId: string]: string | ReadonlyArray<string>;
|
||||
};
|
||||
getPluralForm?: (n: number) => number;
|
||||
};
|
||||
};
|
||||
78
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/locale.js
generated
vendored
Normal file
78
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/locale.js
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @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/locale
|
||||
*/
|
||||
/* globals console */
|
||||
import toArray from './toarray.js';
|
||||
import { _translate, _unifyTranslations } from './translation-service.js';
|
||||
import { getLanguageDirection } from './language.js';
|
||||
/**
|
||||
* Represents the localization services.
|
||||
*/
|
||||
export default class Locale {
|
||||
/**
|
||||
* Creates a new instance of the locale class. Learn more about
|
||||
* {@glink features/ui-language configuring the language of the editor}.
|
||||
*
|
||||
* @param options Locale configuration.
|
||||
* @param options.uiLanguage The editor UI language code in the
|
||||
* [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. See {@link #uiLanguage}.
|
||||
* @param options.contentLanguage The editor content language code in the
|
||||
* [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. If not specified, the same as `options.language`.
|
||||
* See {@link #contentLanguage}.
|
||||
* @param translations Translations passed as a editor config parameter.
|
||||
*/
|
||||
constructor({ uiLanguage = 'en', contentLanguage, translations } = {}) {
|
||||
this.uiLanguage = uiLanguage;
|
||||
this.contentLanguage = contentLanguage || this.uiLanguage;
|
||||
this.uiLanguageDirection = getLanguageDirection(this.uiLanguage);
|
||||
this.contentLanguageDirection = getLanguageDirection(this.contentLanguage);
|
||||
this.translations = _unifyTranslations(translations);
|
||||
this.t = (message, values) => this._t(message, values);
|
||||
}
|
||||
/**
|
||||
* The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
|
||||
*
|
||||
* **Note**: This property was deprecated. Please use {@link #uiLanguage} and {@link #contentLanguage}
|
||||
* properties instead.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
get language() {
|
||||
/**
|
||||
* The {@link module:utils/locale~Locale#language `Locale#language`} property was deprecated and will
|
||||
* be removed in the near future. Please use the {@link module:utils/locale~Locale#uiLanguage `Locale#uiLanguage`} and
|
||||
* {@link module:utils/locale~Locale#contentLanguage `Locale#contentLanguage`} properties instead.
|
||||
*
|
||||
* @error locale-deprecated-language-property
|
||||
*/
|
||||
console.warn('locale-deprecated-language-property: ' +
|
||||
'The Locale#language property has been deprecated and will be removed in the near future. ' +
|
||||
'Please use #uiLanguage and #contentLanguage properties instead.');
|
||||
return this.uiLanguage;
|
||||
}
|
||||
/**
|
||||
* An unbound version of the {@link #t} method.
|
||||
*/
|
||||
_t(message, values = []) {
|
||||
values = toArray(values);
|
||||
if (typeof message === 'string') {
|
||||
message = { string: message };
|
||||
}
|
||||
const hasPluralForm = !!message.plural;
|
||||
const quantity = hasPluralForm ? values[0] : 1;
|
||||
const translatedString = _translate(this.uiLanguage, message, quantity, this.translations);
|
||||
return interpolateString(translatedString, values);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fills the `%0, %1, ...` string placeholders with values.
|
||||
*/
|
||||
function interpolateString(string, values) {
|
||||
return string.replace(/%(\d+)/g, (match, index) => {
|
||||
return (index < values.length) ? values[index] : match;
|
||||
});
|
||||
}
|
||||
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mapsequal.d.ts
generated
vendored
Normal file
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mapsequal.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @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/mapsequal
|
||||
*/
|
||||
/**
|
||||
* Checks whether given `Map`s are equal, that is has same size and same key-value pairs.
|
||||
*
|
||||
* @param mapA The first map to compare.
|
||||
* @param mapB The second map to compare.
|
||||
* @returns `true` if given maps are equal, `false` otherwise.
|
||||
*/
|
||||
export default function mapsEqual<Key>(mapA: Map<Key, unknown>, mapB: Map<Key, unknown>): boolean;
|
||||
27
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mapsequal.js
generated
vendored
Normal file
27
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mapsequal.js
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @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/mapsequal
|
||||
*/
|
||||
/**
|
||||
* Checks whether given `Map`s are equal, that is has same size and same key-value pairs.
|
||||
*
|
||||
* @param mapA The first map to compare.
|
||||
* @param mapB The second map to compare.
|
||||
* @returns `true` if given maps are equal, `false` otherwise.
|
||||
*/
|
||||
export default function mapsEqual(mapA, mapB) {
|
||||
if (mapA.size != mapB.size) {
|
||||
return false;
|
||||
}
|
||||
for (const attr of mapA.entries()) {
|
||||
const valA = JSON.stringify(attr[1]);
|
||||
const valB = JSON.stringify(mapB.get(attr[0]));
|
||||
if (valA !== valB) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
85
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mix.d.ts
generated
vendored
Normal file
85
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mix.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @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/mix
|
||||
*/
|
||||
/**
|
||||
* Copies enumerable properties and symbols from the objects given as 2nd+ parameters to the
|
||||
* prototype of first object (a constructor).
|
||||
*
|
||||
* ```
|
||||
* class Editor {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* const SomeMixin = {
|
||||
* a() {
|
||||
* return 'a';
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* mix( Editor, SomeMixin, ... );
|
||||
*
|
||||
* new Editor().a(); // -> 'a'
|
||||
* ```
|
||||
*
|
||||
* Note: Properties which already exist in the base class will not be overriden.
|
||||
*
|
||||
* @deprecated Use mixin pattern, see: https://www.typescriptlang.org/docs/handbook/mixins.html.
|
||||
* @param baseClass Class which prototype will be extended.
|
||||
* @param mixins Objects from which to get properties.
|
||||
*/
|
||||
export default function mix(baseClass: Function, ...mixins: Array<object>): void;
|
||||
/**
|
||||
* Helper type that represents constructor creating given objects. Can be used as a type constraint.
|
||||
*
|
||||
* ```ts
|
||||
* // The function accepts any class constructor.
|
||||
* function MyFunction<Ctor extends Constructor>( ctor: Ctor ) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // The function accepts any class constructor of type derived from `MyBase`.
|
||||
* function MyFunction<Ctor extends Constructor<MyBase>>( ctor: Ctor ) {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type Constructor<Instance = object> = abstract new (...args: Array<any>) => Instance;
|
||||
/**
|
||||
* Helper type that creates constructor types from a base class and a mixin interface.
|
||||
*
|
||||
* ```ts
|
||||
* interface MyMixinInterface {
|
||||
* mixinMethod(): void;
|
||||
* }
|
||||
*
|
||||
* function MyMixin<Base extends Constructor>( base: Base ): Mixed<Base, MyMixinInterface> {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* class BaseClass {
|
||||
* baseMethod(): void {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const MixedClass = MyMixin( BaseClass );
|
||||
*
|
||||
* // Contains both `mixinMethod()` and `baseMethod()`.
|
||||
* const myObject = new MixedClass();
|
||||
* myObject.mixinMethod();
|
||||
* myObject.baseMethod();
|
||||
* ```
|
||||
*
|
||||
* @typeParam Base A type of constructor of a class to apply mixin to.
|
||||
* @typeParam Mixin An interface representing mixin.
|
||||
*/
|
||||
export type Mixed<Base extends Constructor, Mixin extends object> = {
|
||||
new (...args: ConstructorParameters<Base>): Mixin & InstanceType<Base>;
|
||||
prototype: Mixin & InstanceType<Base>;
|
||||
} & {
|
||||
[K in keyof Base]: Base[K];
|
||||
};
|
||||
50
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mix.js
generated
vendored
Normal file
50
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/mix.js
generated
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @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/mix
|
||||
*/
|
||||
/**
|
||||
* Copies enumerable properties and symbols from the objects given as 2nd+ parameters to the
|
||||
* prototype of first object (a constructor).
|
||||
*
|
||||
* ```
|
||||
* class Editor {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* const SomeMixin = {
|
||||
* a() {
|
||||
* return 'a';
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* mix( Editor, SomeMixin, ... );
|
||||
*
|
||||
* new Editor().a(); // -> 'a'
|
||||
* ```
|
||||
*
|
||||
* Note: Properties which already exist in the base class will not be overriden.
|
||||
*
|
||||
* @deprecated Use mixin pattern, see: https://www.typescriptlang.org/docs/handbook/mixins.html.
|
||||
* @param baseClass Class which prototype will be extended.
|
||||
* @param mixins Objects from which to get properties.
|
||||
*/
|
||||
export default function mix(baseClass, ...mixins) {
|
||||
mixins.forEach(mixin => {
|
||||
const propertyNames = Object.getOwnPropertyNames(mixin);
|
||||
const propertySymbols = Object.getOwnPropertySymbols(mixin);
|
||||
propertyNames.concat(propertySymbols).forEach(key => {
|
||||
if (key in baseClass.prototype) {
|
||||
return;
|
||||
}
|
||||
if (typeof mixin == 'function' && (key == 'length' || key == 'name' || key == 'prototype')) {
|
||||
return;
|
||||
}
|
||||
const sourceDescriptor = Object.getOwnPropertyDescriptor(mixin, key);
|
||||
sourceDescriptor.enumerable = false;
|
||||
Object.defineProperty(baseClass.prototype, key, sourceDescriptor);
|
||||
});
|
||||
});
|
||||
}
|
||||
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/nth.d.ts
generated
vendored
Normal file
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/nth.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @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/nth
|
||||
*/
|
||||
/**
|
||||
* Returns `nth` (starts from `0` of course) item of the given `iterable`.
|
||||
*
|
||||
* If the iterable is a generator, then it consumes **all its items**.
|
||||
* If it's a normal iterator, then it consumes **all items up to the given index**.
|
||||
* Refer to the [Iterators and Generators](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Iterators_and_Generators)
|
||||
* guide to learn differences between these interfaces.
|
||||
*/
|
||||
export default function nth<T>(index: number, iterable: Iterable<T>): T | null;
|
||||
24
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/nth.js
generated
vendored
Normal file
24
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/nth.js
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @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/nth
|
||||
*/
|
||||
/**
|
||||
* Returns `nth` (starts from `0` of course) item of the given `iterable`.
|
||||
*
|
||||
* If the iterable is a generator, then it consumes **all its items**.
|
||||
* If it's a normal iterator, then it consumes **all items up to the given index**.
|
||||
* Refer to the [Iterators and Generators](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Iterators_and_Generators)
|
||||
* guide to learn differences between these interfaces.
|
||||
*/
|
||||
export default function nth(index, iterable) {
|
||||
for (const item of iterable) {
|
||||
if (index === 0) {
|
||||
return item;
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
23
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/objecttomap.d.ts
generated
vendored
Normal file
23
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/objecttomap.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @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/objecttomap
|
||||
*/
|
||||
/**
|
||||
* Transforms object to map.
|
||||
*
|
||||
* ```ts
|
||||
* const map = objectToMap( { 'foo': 1, 'bar': 2 } );
|
||||
* map.get( 'foo' ); // 1
|
||||
* ```
|
||||
*
|
||||
* **Note**: For mixed data (`Object` or `Iterable`) there's a dedicated {@link module:utils/tomap~toMap} function.
|
||||
*
|
||||
* @param obj Object to transform.
|
||||
* @returns Map created from object.
|
||||
*/
|
||||
export default function objectToMap<T>(obj: {
|
||||
readonly [key: string]: T;
|
||||
} | null | undefined): Map<string, T>;
|
||||
27
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/objecttomap.js
generated
vendored
Normal file
27
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/objecttomap.js
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @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/objecttomap
|
||||
*/
|
||||
/**
|
||||
* Transforms object to map.
|
||||
*
|
||||
* ```ts
|
||||
* const map = objectToMap( { 'foo': 1, 'bar': 2 } );
|
||||
* map.get( 'foo' ); // 1
|
||||
* ```
|
||||
*
|
||||
* **Note**: For mixed data (`Object` or `Iterable`) there's a dedicated {@link module:utils/tomap~toMap} function.
|
||||
*
|
||||
* @param obj Object to transform.
|
||||
* @returns Map created from object.
|
||||
*/
|
||||
export default function objectToMap(obj) {
|
||||
const map = new Map();
|
||||
for (const key in obj) {
|
||||
map.set(key, obj[key]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
560
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.d.ts
generated
vendored
Normal file
560
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
/**
|
||||
* @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/observablemixin
|
||||
*/
|
||||
import { type Emitter } from '@ckeditor/ckeditor5-utils/src/emittermixin.js';
|
||||
import type { Constructor, Mixed } from '@ckeditor/ckeditor5-utils/src/mix.js';
|
||||
/**
|
||||
* A mixin that injects the "observable properties" and data binding functionality described in the
|
||||
* {@link ~Observable} interface.
|
||||
*
|
||||
* This function creates a class that inherits from the provided `base` and implements `Observable` interface.
|
||||
*
|
||||
* ```ts
|
||||
* class BaseClass { ... }
|
||||
*
|
||||
* class MyClass extends ObservableMixin( BaseClass ) {
|
||||
* // This class derives from `BaseClass` and implements the `Observable` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the concept of observables in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide,
|
||||
* * {@glink framework/deep-dive/observables Observables deep-dive} guide.
|
||||
*
|
||||
* @label EXTENDS
|
||||
*/
|
||||
export default function ObservableMixin<Base extends Constructor<Emitter>>(base: Base): Mixed<Base, Observable>;
|
||||
/**
|
||||
* A mixin that injects the "observable properties" and data binding functionality described in the
|
||||
* {@link ~Observable} interface.
|
||||
*
|
||||
* This function creates a class that implements `Observable` interface.
|
||||
*
|
||||
* ```ts
|
||||
* class MyClass extends ObservableMixin() {
|
||||
* // This class implements the `Observable` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the concept of observables in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide,
|
||||
* * {@glink framework/deep-dive/observables Observables deep dive} guide.
|
||||
*
|
||||
* @label NO_ARGUMENTS
|
||||
*/
|
||||
export default function ObservableMixin(): {
|
||||
new (): Observable;
|
||||
prototype: Observable;
|
||||
};
|
||||
/**
|
||||
* An interface which adds "observable properties" and data binding functionality.
|
||||
*
|
||||
* Can be easily implemented by a class by mixing the {@link module:utils/observablemixin~Observable} mixin.
|
||||
*
|
||||
* ```ts
|
||||
* class MyClass extends ObservableMixin( OtherBaseClass ) {
|
||||
* // This class now implements the `Observable` interface.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Read more about the usage of this interface in the:
|
||||
* * {@glink framework/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
|
||||
* section of the {@glink framework/architecture/core-editor-architecture Core editor architecture} guide,
|
||||
* * {@glink framework/deep-dive/observables Observables deep-dive} guide.
|
||||
*/
|
||||
export interface Observable extends Emitter {
|
||||
/**
|
||||
* Creates and sets the value of an observable property of this object. Such a property becomes a part
|
||||
* of the state and is observable.
|
||||
*
|
||||
* This method throws the `observable-set-cannot-override` error if the observable instance already
|
||||
* has a property with the given property name. This prevents from mistakenly overriding existing
|
||||
* properties and methods, but means that `foo.set( 'bar', 1 )` may be slightly slower than `foo.bar = 1`.
|
||||
*
|
||||
* In TypeScript, those properties should be declared in class using `declare` keyword. In example:
|
||||
*
|
||||
* ```ts
|
||||
* public declare myProp: number;
|
||||
*
|
||||
* constructor() {
|
||||
* this.set( 'myProp', 2 );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @label KEY_VALUE
|
||||
* @param name The property's name.
|
||||
* @param value The property's value.
|
||||
*/
|
||||
set<K extends keyof this & string>(name: K, value: this[K]): void;
|
||||
/**
|
||||
* Creates and sets the value of an observable properties of this object. Such a property becomes a part
|
||||
* of the state and is observable.
|
||||
*
|
||||
* It accepts a single object literal containing key/value pairs with properties to be set.
|
||||
*
|
||||
* This method throws the `observable-set-cannot-override` error if the observable instance already
|
||||
* has a property with the given property name. This prevents from mistakenly overriding existing
|
||||
* properties and methods, but means that `foo.set( 'bar', 1 )` may be slightly slower than `foo.bar = 1`.
|
||||
*
|
||||
* In TypeScript, those properties should be declared in class using `declare` keyword. In example:
|
||||
*
|
||||
* ```ts
|
||||
* public declare myProp1: number;
|
||||
* public declare myProp2: string;
|
||||
*
|
||||
* constructor() {
|
||||
* this.set( {
|
||||
* 'myProp1: 2,
|
||||
* 'myProp2: 'foo'
|
||||
* } );
|
||||
* }
|
||||
* ```
|
||||
* @label OBJECT
|
||||
* @param values An object with `name=>value` pairs.
|
||||
*/
|
||||
set(values: object & {
|
||||
readonly [K in keyof this]?: unknown;
|
||||
}): void;
|
||||
/**
|
||||
* Binds {@link #set observable properties} to other objects implementing the
|
||||
* {@link module:utils/observablemixin~Observable} interface.
|
||||
*
|
||||
* Read more in the {@glink framework/deep-dive/observables#property-bindings dedicated} guide
|
||||
* covering the topic of property bindings with some additional examples.
|
||||
*
|
||||
* Consider two objects: a `button` and an associated `command` (both `Observable`).
|
||||
*
|
||||
* A simple property binding could be as follows:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled' );
|
||||
* ```
|
||||
*
|
||||
* or even shorter:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which works in the following way:
|
||||
*
|
||||
* * `button.isEnabled` **instantly equals** `command.isEnabled`,
|
||||
* * whenever `command.isEnabled` changes, `button.isEnabled` will immediately reflect its value.
|
||||
*
|
||||
* **Note**: To release the binding, use {@link module:utils/observablemixin~Observable#unbind}.
|
||||
*
|
||||
* You can also "rename" the property in the binding by specifying the new name in the `to()` chain:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isWorking' );
|
||||
* ```
|
||||
*
|
||||
* It is possible to bind more than one property at a time to shorten the code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled', 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which corresponds to:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* button.bind( 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* The binding can include more than one observable, combining multiple data sources in a custom callback:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled', ui, 'isVisible',
|
||||
* ( isCommandEnabled, isUIVisible ) => isCommandEnabled && isUIVisible );
|
||||
* ```
|
||||
*
|
||||
* Using a custom callback allows processing the value before passing it to the target property:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'value', value => value === 'heading1' );
|
||||
* ```
|
||||
*
|
||||
* It is also possible to bind to the same property in an array of observables.
|
||||
* To bind a `button` to multiple commands (also `Observables`) so that each and every one of them
|
||||
* must be enabled for the button to become enabled, use the following code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).toMany( [ commandA, commandB, commandC ], 'isEnabled',
|
||||
* ( isAEnabled, isBEnabled, isCEnabled ) => isAEnabled && isBEnabled && isCEnabled );
|
||||
* ```
|
||||
*
|
||||
* @label SINGLE_BIND
|
||||
* @param bindProperty Observable property that will be bound to other observable(s).
|
||||
* @returns The bind chain with the `to()` and `toMany()` methods.
|
||||
*/
|
||||
bind<K extends keyof this & string>(bindProperty: K): SingleBindChain<K, this[K]>;
|
||||
/**
|
||||
* Binds {@link #set observable properties} to other objects implementing the
|
||||
* {@link module:utils/observablemixin~Observable} interface.
|
||||
*
|
||||
* Read more in the {@glink framework/deep-dive/observables#property-bindings dedicated} guide
|
||||
* covering the topic of property bindings with some additional examples.
|
||||
*
|
||||
* Consider two objects: a `button` and an associated `command` (both `Observable`).
|
||||
*
|
||||
* A simple property binding could be as follows:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled' );
|
||||
* ```
|
||||
*
|
||||
* or even shorter:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which works in the following way:
|
||||
*
|
||||
* * `button.isEnabled` **instantly equals** `command.isEnabled`,
|
||||
* * whenever `command.isEnabled` changes, `button.isEnabled` will immediately reflect its value.
|
||||
*
|
||||
* **Note**: To release the binding, use {@link module:utils/observablemixin~Observable#unbind}.
|
||||
*
|
||||
* You can also "rename" the property in the binding by specifying the new name in the `to()` chain:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isWorking' );
|
||||
* ```
|
||||
*
|
||||
* It is possible to bind more than one property at a time to shorten the code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled', 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which corresponds to:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* button.bind( 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* The binding can include more than one observable, combining multiple data sources in a custom callback:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled', ui, 'isVisible',
|
||||
* ( isCommandEnabled, isUIVisible ) => isCommandEnabled && isUIVisible );
|
||||
* ```
|
||||
*
|
||||
* Using a custom callback allows processing the value before passing it to the target property:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'value', value => value === 'heading1' );
|
||||
* ```
|
||||
*
|
||||
* It is also possible to bind to the same property in an array of observables.
|
||||
* To bind a `button` to multiple commands (also `Observables`) so that each and every one of them
|
||||
* must be enabled for the button to become enabled, use the following code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).toMany( [ commandA, commandB, commandC ], 'isEnabled',
|
||||
* ( isAEnabled, isBEnabled, isCEnabled ) => isAEnabled && isBEnabled && isCEnabled );
|
||||
* ```
|
||||
*
|
||||
* @label DUAL_BIND
|
||||
* @param bindProperty1 Observable property that will be bound to other observable(s).
|
||||
* @param bindProperty2 Observable property that will be bound to other observable(s).
|
||||
* @returns The bind chain with the `to()` and `toMany()` methods.
|
||||
*/
|
||||
bind<K1 extends keyof this & string, K2 extends keyof this & string>(bindProperty1: K1, bindProperty2: K2): DualBindChain<K1, this[K1], K2, this[K2]>;
|
||||
/**
|
||||
* Binds {@link #set observable properties} to other objects implementing the
|
||||
* {@link module:utils/observablemixin~Observable} interface.
|
||||
*
|
||||
* Read more in the {@glink framework/deep-dive/observables#property-bindings dedicated} guide
|
||||
* covering the topic of property bindings with some additional examples.
|
||||
*
|
||||
* Consider two objects: a `button` and an associated `command` (both `Observable`).
|
||||
*
|
||||
* A simple property binding could be as follows:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled' );
|
||||
* ```
|
||||
*
|
||||
* or even shorter:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which works in the following way:
|
||||
*
|
||||
* * `button.isEnabled` **instantly equals** `command.isEnabled`,
|
||||
* * whenever `command.isEnabled` changes, `button.isEnabled` will immediately reflect its value.
|
||||
*
|
||||
* **Note**: To release the binding, use {@link module:utils/observablemixin~Observable#unbind}.
|
||||
*
|
||||
* You can also "rename" the property in the binding by specifying the new name in the `to()` chain:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isWorking' );
|
||||
* ```
|
||||
*
|
||||
* It is possible to bind more than one property at a time to shorten the code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled', 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* which corresponds to:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command );
|
||||
* button.bind( 'value' ).to( command );
|
||||
* ```
|
||||
*
|
||||
* The binding can include more than one observable, combining multiple data sources in a custom callback:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'isEnabled', ui, 'isVisible',
|
||||
* ( isCommandEnabled, isUIVisible ) => isCommandEnabled && isUIVisible );
|
||||
* ```
|
||||
*
|
||||
* Using a custom callback allows processing the value before passing it to the target property:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).to( command, 'value', value => value === 'heading1' );
|
||||
* ```
|
||||
*
|
||||
* It is also possible to bind to the same property in an array of observables.
|
||||
* To bind a `button` to multiple commands (also `Observables`) so that each and every one of them
|
||||
* must be enabled for the button to become enabled, use the following code:
|
||||
*
|
||||
* ```ts
|
||||
* button.bind( 'isEnabled' ).toMany( [ commandA, commandB, commandC ], 'isEnabled',
|
||||
* ( isAEnabled, isBEnabled, isCEnabled ) => isAEnabled && isBEnabled && isCEnabled );
|
||||
* ```
|
||||
*
|
||||
* @label MANY_BIND
|
||||
* @param bindProperties Observable properties that will be bound to other observable(s).
|
||||
* @returns The bind chain with the `to()` and `toMany()` methods.
|
||||
*/
|
||||
bind(...bindProperties: Array<keyof this & string>): MultiBindChain;
|
||||
/**
|
||||
* Removes the binding created with {@link #bind}.
|
||||
*
|
||||
* ```ts
|
||||
* // Removes the binding for the 'a' property.
|
||||
* A.unbind( 'a' );
|
||||
*
|
||||
* // Removes bindings for all properties.
|
||||
* A.unbind();
|
||||
* ```
|
||||
*
|
||||
* @param unbindProperties Observable properties to be unbound. All the bindings will
|
||||
* be released if no properties are provided.
|
||||
*/
|
||||
unbind(...unbindProperties: Array<keyof this & string>): void;
|
||||
/**
|
||||
* Turns the given methods of this object into event-based ones. This means that the new method will fire an event
|
||||
* (named after the method) and the original action will be plugged as a listener to that event.
|
||||
*
|
||||
* Read more in the {@glink framework/deep-dive/observables#decorating-object-methods dedicated} guide
|
||||
* covering the topic of decorating methods with some additional examples.
|
||||
*
|
||||
* Decorating the method does not change its behavior (it only adds an event),
|
||||
* but it allows to modify it later on by listening to the method's event.
|
||||
*
|
||||
* For example, to cancel the method execution the event can be {@link module:utils/eventinfo~EventInfo#stop stopped}:
|
||||
*
|
||||
* ```ts
|
||||
* class Foo extends ObservableMixin() {
|
||||
* constructor() {
|
||||
* super();
|
||||
* this.decorate( 'method' );
|
||||
* }
|
||||
*
|
||||
* method() {
|
||||
* console.log( 'called!' );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* const foo = new Foo();
|
||||
* foo.on( 'method', ( evt ) => {
|
||||
* evt.stop();
|
||||
* }, { priority: 'high' } );
|
||||
*
|
||||
* foo.method(); // Nothing is logged.
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* **Note**: The high {@link module:utils/priorities~PriorityString priority} listener
|
||||
* has been used to execute this particular callback before the one which calls the original method
|
||||
* (which uses the "normal" priority).
|
||||
*
|
||||
* It is also possible to change the returned value:
|
||||
*
|
||||
* ```ts
|
||||
* foo.on( 'method', ( evt ) => {
|
||||
* evt.return = 'Foo!';
|
||||
* } );
|
||||
*
|
||||
* foo.method(); // -> 'Foo'
|
||||
* ```
|
||||
*
|
||||
* Finally, it is possible to access and modify the arguments the method is called with:
|
||||
*
|
||||
* ```ts
|
||||
* method( a, b ) {
|
||||
* console.log( `${ a }, ${ b }` );
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* foo.on( 'method', ( evt, args ) => {
|
||||
* args[ 0 ] = 3;
|
||||
*
|
||||
* console.log( args[ 1 ] ); // -> 2
|
||||
* }, { priority: 'high' } );
|
||||
*
|
||||
* foo.method( 1, 2 ); // -> '3, 2'
|
||||
* ```
|
||||
*
|
||||
* @param methodName Name of the method to decorate.
|
||||
*/
|
||||
decorate(methodName: keyof this & string): void;
|
||||
}
|
||||
/**
|
||||
* Fired when a property changed value.
|
||||
*
|
||||
* ```ts
|
||||
* observable.set( 'prop', 1 );
|
||||
*
|
||||
* observable.on<ObservableChangeEvent<number>>( 'change:prop', ( evt, propertyName, newValue, oldValue ) => {
|
||||
* console.log( `${ propertyName } has changed from ${ oldValue } to ${ newValue }` );
|
||||
* } );
|
||||
*
|
||||
* observable.prop = 2; // -> 'prop has changed from 1 to 2'
|
||||
* ```
|
||||
*
|
||||
* @eventName ~Observable#change:\{property\}
|
||||
* @param {String} name The property name.
|
||||
* @param {*} value The new property value.
|
||||
* @param {*} oldValue The previous property value.
|
||||
*/
|
||||
export type ObservableChangeEvent<TValue = any> = {
|
||||
name: 'change' | `change:${string}`;
|
||||
args: [name: string, value: TValue, oldValue: TValue];
|
||||
};
|
||||
/**
|
||||
* Fired when a property value is going to be set but is not set yet (before the `change` event is fired).
|
||||
*
|
||||
* You can control the final value of the property by using
|
||||
* the {@link module:utils/eventinfo~EventInfo#return event's `return` property}.
|
||||
*
|
||||
* ```ts
|
||||
* observable.set( 'prop', 1 );
|
||||
*
|
||||
* observable.on<ObservableSetEvent<number>>( 'set:prop', ( evt, propertyName, newValue, oldValue ) => {
|
||||
* console.log( `Value is going to be changed from ${ oldValue } to ${ newValue }` );
|
||||
* console.log( `Current property value is ${ observable[ propertyName ] }` );
|
||||
*
|
||||
* // Let's override the value.
|
||||
* evt.return = 3;
|
||||
* } );
|
||||
*
|
||||
* observable.on<ObservableChangeEvent<number>>( 'change:prop', ( evt, propertyName, newValue, oldValue ) => {
|
||||
* console.log( `Value has changed from ${ oldValue } to ${ newValue }` );
|
||||
* } );
|
||||
*
|
||||
* observable.prop = 2; // -> 'Value is going to be changed from 1 to 2'
|
||||
* // -> 'Current property value is 1'
|
||||
* // -> 'Value has changed from 1 to 3'
|
||||
* ```
|
||||
*
|
||||
* **Note:** The event is fired even when the new value is the same as the old value.
|
||||
*
|
||||
* @eventName ~Observable#set:\{property\}
|
||||
* @param {String} name The property name.
|
||||
* @param {*} value The new property value.
|
||||
* @param {*} oldValue The previous property value.
|
||||
*/
|
||||
export type ObservableSetEvent<TValue = any> = {
|
||||
name: 'set' | `set:${string}`;
|
||||
args: [name: string, value: TValue, oldValue: TValue];
|
||||
return: TValue;
|
||||
};
|
||||
/**
|
||||
* Utility type that creates an event describing type from decorated method.
|
||||
*
|
||||
* ```ts
|
||||
* class Foo extends ObservableMixin() {
|
||||
* constructor() {
|
||||
* super();
|
||||
* this.decorate( 'method' );
|
||||
* }
|
||||
*
|
||||
* method( a: number, b: number ): number {
|
||||
* return a + b;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* type FooMethodEvent = DecoratedMethodEvent<Foo, 'method'>;
|
||||
*
|
||||
* const foo = new Foo();
|
||||
*
|
||||
* foo.on<FooMethodEvent>( 'method', ( evt, [ a, b ] ) => {
|
||||
* // `a` and `b` are inferred as numbers.
|
||||
* } )
|
||||
* ```
|
||||
*/
|
||||
export type DecoratedMethodEvent<TObservable extends Observable & {
|
||||
[N in TName]: (...args: Array<any>) => any;
|
||||
}, TName extends keyof TObservable & string> = {
|
||||
name: TName;
|
||||
args: [Parameters<TObservable[TName]>];
|
||||
return: ReturnType<TObservable[TName]>;
|
||||
};
|
||||
interface SingleBindChain<TKey extends string, TVal> {
|
||||
toMany<O extends Observable, K extends keyof O>(observables: ReadonlyArray<O>, key: K, callback: (...values: Array<O[K]>) => TVal): void;
|
||||
to<O extends ObservableWithProperty<TKey, TVal>>(observable: O): void;
|
||||
to<O extends ObservableWithProperty<TKey>>(observable: O, callback: (value: O[TKey]) => TVal): void;
|
||||
to<O extends ObservableWithProperty<K, TVal>, K extends keyof O>(observable: O, key: K): void;
|
||||
to<O extends Observable, K extends keyof O>(observable: O, key: K, callback: (value: O[K]) => TVal): void;
|
||||
to<O1 extends ObservableWithProperty<TKey>, O2 extends ObservableWithProperty<TKey>>(observable1: O1, observable2: O2, callback: (value1: O1[TKey], value2: O2[TKey]) => TVal): void;
|
||||
to<O1 extends Observable, K1 extends keyof O1, O2 extends Observable, K2 extends keyof O2>(observable1: O1, key1: K1, observable2: O2, key2: K2, callback: (value1: O1[K1], value2: O2[K2]) => TVal): void;
|
||||
to<O1 extends ObservableWithProperty<TKey>, O2 extends ObservableWithProperty<TKey>, O3 extends ObservableWithProperty<TKey>>(observable1: O1, observable2: O2, observable3: O3, callback: (value1: O1[TKey], value2: O2[TKey], value3: O3[TKey]) => TVal): void;
|
||||
to<O1 extends Observable, K1 extends keyof O1, O2 extends Observable, K2 extends keyof O2, O3 extends Observable, K3 extends keyof O3>(observable1: O1, key1: K1, observable2: O2, key2: K2, observable3: O3, key3: K3, callback: (value1: O1[K1], value2: O2[K2], value3: O3[K3]) => TVal): void;
|
||||
to<O1 extends ObservableWithProperty<TKey>, O2 extends ObservableWithProperty<TKey>, O3 extends ObservableWithProperty<TKey>, O4 extends ObservableWithProperty<TKey>>(observable1: O1, observable2: O2, observable3: O3, observable4: O4, callback: (value1: O1[TKey], value2: O2[TKey], value3: O3[TKey], value4: O4[TKey]) => TVal): void;
|
||||
to<O1 extends Observable, K1 extends keyof O1, O2 extends Observable, K2 extends keyof O2, O3 extends Observable, K3 extends keyof O3, O4 extends Observable, K4 extends keyof O4>(observable1: O1, key1: K1, observable2: O2, key2: K2, observable3: O3, key3: K3, observable4: O4, key4: K4, callback: (value1: O1[K1], value2: O2[K2], value3: O3[K3], value4: O4[K4]) => TVal): void;
|
||||
to<O1 extends ObservableWithProperty<TKey>, O2 extends ObservableWithProperty<TKey>, O3 extends ObservableWithProperty<TKey>, O4 extends ObservableWithProperty<TKey>, O5 extends ObservableWithProperty<TKey>>(observable1: O1, observable2: O2, observable3: O3, observable4: O4, observable5: O5, callback: (value1: O1[TKey], value2: O2[TKey], value3: O3[TKey], value4: O4[TKey], value5: O5[TKey]) => TVal): void;
|
||||
to<O1 extends Observable, K1 extends keyof O1, O2 extends Observable, K2 extends keyof O2, O3 extends Observable, K3 extends keyof O3, O4 extends Observable, K4 extends keyof O4, O5 extends Observable, K5 extends keyof O5>(observable1: O1, key1: K1, observable2: O2, key2: K2, observable3: O3, key3: K3, observable4: O4, key4: K4, observable5: O5, key5: K5, callback: (value1: O1[K1], value2: O2[K2], value3: O3[K3], value4: O4[K4], value5: O5[K5]) => TVal): void;
|
||||
}
|
||||
/**
|
||||
* A helper type that can be used as a constraint, ensuring the type is both observable and have the given property.
|
||||
*
|
||||
* ```ts
|
||||
* // Ensures that `obj` is `Observable` and have property named 'abc'.
|
||||
* function f<O extends ObservableWithProperty<'abc'>>( obj: O ) {}
|
||||
*
|
||||
* // Ensures that `obj` is `Observable` and have property named 'abc' with value `number`.
|
||||
* function f<O extends ObservableWithProperty<'abc', number>>( obj: O ) {}
|
||||
* ```
|
||||
*/
|
||||
export type ObservableWithProperty<TKey extends PropertyKey, TVal = any> = undefined extends TVal ? Observable & {
|
||||
[P in TKey]?: TVal;
|
||||
} : Observable & {
|
||||
[P in TKey]: TVal;
|
||||
};
|
||||
interface DualBindChain<TKey1 extends string, TVal1, TKey2 extends string, TVal2> {
|
||||
to<O extends ObservableWithProperty<K1, TVal1> & ObservableWithProperty<K2, TVal2>, K1 extends keyof O, K2 extends keyof O>(observable: O, key1: K1, key2: K2): void;
|
||||
to<O extends ObservableWithProperty<TKey1, TVal1> & ObservableWithProperty<TKey2, TVal2>>(observable: O): void;
|
||||
}
|
||||
interface MultiBindChain {
|
||||
to<O extends Observable>(observable: O, ...properties: Array<keyof O>): void;
|
||||
}
|
||||
export {};
|
||||
580
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.js
generated
vendored
Normal file
580
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.js
generated
vendored
Normal file
|
|
@ -0,0 +1,580 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/unified-signatures */
|
||||
/**
|
||||
* @module utils/observablemixin
|
||||
*/
|
||||
import EmitterMixin from './emittermixin.js';
|
||||
import CKEditorError from './ckeditorerror.js';
|
||||
import { isObject } from 'lodash-es';
|
||||
const observablePropertiesSymbol = Symbol('observableProperties');
|
||||
const boundObservablesSymbol = Symbol('boundObservables');
|
||||
const boundPropertiesSymbol = Symbol('boundProperties');
|
||||
const decoratedMethods = Symbol('decoratedMethods');
|
||||
const decoratedOriginal = Symbol('decoratedOriginal');
|
||||
const defaultObservableClass = ObservableMixin(EmitterMixin());
|
||||
export default function ObservableMixin(base) {
|
||||
if (!base) {
|
||||
return defaultObservableClass;
|
||||
}
|
||||
class Mixin extends base {
|
||||
set(name, value) {
|
||||
// If the first parameter is an Object, iterate over its properties.
|
||||
if (isObject(name)) {
|
||||
Object.keys(name).forEach(property => {
|
||||
this.set(property, name[property]);
|
||||
}, this);
|
||||
return;
|
||||
}
|
||||
initObservable(this);
|
||||
const properties = this[observablePropertiesSymbol];
|
||||
if ((name in this) && !properties.has(name)) {
|
||||
/**
|
||||
* Cannot override an existing property.
|
||||
*
|
||||
* This error is thrown when trying to {@link module:utils/observablemixin~Observable#set set} a property with
|
||||
* a name of an already existing property. For example:
|
||||
*
|
||||
* ```ts
|
||||
* let observable = new Model();
|
||||
* observable.property = 1;
|
||||
* observable.set( 'property', 2 ); // throws
|
||||
*
|
||||
* observable.set( 'property', 1 );
|
||||
* observable.set( 'property', 2 ); // ok, because this is an existing property.
|
||||
* ```
|
||||
*
|
||||
* @error observable-set-cannot-override
|
||||
*/
|
||||
throw new CKEditorError('observable-set-cannot-override', this);
|
||||
}
|
||||
Object.defineProperty(this, name, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
return properties.get(name);
|
||||
},
|
||||
set(value) {
|
||||
const oldValue = properties.get(name);
|
||||
// Fire `set` event before the new value will be set to make it possible
|
||||
// to override observable property without affecting `change` event.
|
||||
// See https://github.com/ckeditor/ckeditor5-utils/issues/171.
|
||||
let newValue = this.fire(`set:${name}`, name, value, oldValue);
|
||||
if (newValue === undefined) {
|
||||
newValue = value;
|
||||
}
|
||||
// Allow undefined as an initial value like A.define( 'x', undefined ) (#132).
|
||||
// Note: When properties map has no such own property, then its value is undefined.
|
||||
if (oldValue !== newValue || !properties.has(name)) {
|
||||
properties.set(name, newValue);
|
||||
this.fire(`change:${name}`, name, newValue, oldValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
this[name] = value;
|
||||
}
|
||||
bind(...bindProperties) {
|
||||
if (!bindProperties.length || !isStringArray(bindProperties)) {
|
||||
/**
|
||||
* All properties must be strings.
|
||||
*
|
||||
* @error observable-bind-wrong-properties
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-wrong-properties', this);
|
||||
}
|
||||
if ((new Set(bindProperties)).size !== bindProperties.length) {
|
||||
/**
|
||||
* Properties must be unique.
|
||||
*
|
||||
* @error observable-bind-duplicate-properties
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-duplicate-properties', this);
|
||||
}
|
||||
initObservable(this);
|
||||
const boundProperties = this[boundPropertiesSymbol];
|
||||
bindProperties.forEach(propertyName => {
|
||||
if (boundProperties.has(propertyName)) {
|
||||
/**
|
||||
* Cannot bind the same property more than once.
|
||||
*
|
||||
* @error observable-bind-rebind
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-rebind', this);
|
||||
}
|
||||
});
|
||||
const bindings = new Map();
|
||||
bindProperties.forEach(a => {
|
||||
const binding = { property: a, to: [] };
|
||||
boundProperties.set(a, binding);
|
||||
bindings.set(a, binding);
|
||||
});
|
||||
return {
|
||||
to: bindTo,
|
||||
toMany: bindToMany,
|
||||
_observable: this,
|
||||
_bindProperties: bindProperties,
|
||||
_to: [],
|
||||
_bindings: bindings
|
||||
};
|
||||
}
|
||||
unbind(...unbindProperties) {
|
||||
// Nothing to do here if not inited yet.
|
||||
if (!(this[observablePropertiesSymbol])) {
|
||||
return;
|
||||
}
|
||||
const boundProperties = this[boundPropertiesSymbol];
|
||||
const boundObservables = this[boundObservablesSymbol];
|
||||
if (unbindProperties.length) {
|
||||
if (!isStringArray(unbindProperties)) {
|
||||
/**
|
||||
* Properties must be strings.
|
||||
*
|
||||
* @error observable-unbind-wrong-properties
|
||||
*/
|
||||
throw new CKEditorError('observable-unbind-wrong-properties', this);
|
||||
}
|
||||
unbindProperties.forEach(propertyName => {
|
||||
const binding = boundProperties.get(propertyName);
|
||||
// Nothing to do if the binding is not defined
|
||||
if (!binding) {
|
||||
return;
|
||||
}
|
||||
binding.to.forEach(([toObservable, toProperty]) => {
|
||||
const toProperties = boundObservables.get(toObservable);
|
||||
const toPropertyBindings = toProperties[toProperty];
|
||||
toPropertyBindings.delete(binding);
|
||||
if (!toPropertyBindings.size) {
|
||||
delete toProperties[toProperty];
|
||||
}
|
||||
if (!Object.keys(toProperties).length) {
|
||||
boundObservables.delete(toObservable);
|
||||
this.stopListening(toObservable, 'change');
|
||||
}
|
||||
});
|
||||
boundProperties.delete(propertyName);
|
||||
});
|
||||
}
|
||||
else {
|
||||
boundObservables.forEach((bindings, boundObservable) => {
|
||||
this.stopListening(boundObservable, 'change');
|
||||
});
|
||||
boundObservables.clear();
|
||||
boundProperties.clear();
|
||||
}
|
||||
}
|
||||
decorate(methodName) {
|
||||
initObservable(this);
|
||||
const originalMethod = this[methodName];
|
||||
if (!originalMethod) {
|
||||
/**
|
||||
* Cannot decorate an undefined method.
|
||||
*
|
||||
* @error observablemixin-cannot-decorate-undefined
|
||||
* @param {Object} object The object which method should be decorated.
|
||||
* @param {String} methodName Name of the method which does not exist.
|
||||
*/
|
||||
throw new CKEditorError('observablemixin-cannot-decorate-undefined', this, { object: this, methodName });
|
||||
}
|
||||
this.on(methodName, (evt, args) => {
|
||||
evt.return = originalMethod.apply(this, args);
|
||||
});
|
||||
this[methodName] = function (...args) {
|
||||
return this.fire(methodName, args);
|
||||
};
|
||||
this[methodName][decoratedOriginal] = originalMethod;
|
||||
if (!this[decoratedMethods]) {
|
||||
this[decoratedMethods] = [];
|
||||
}
|
||||
this[decoratedMethods].push(methodName);
|
||||
}
|
||||
// Override the EmitterMixin stopListening method to be able to clean (and restore) decorated methods.
|
||||
// This is needed in case of:
|
||||
// 1. Have x.foo() decorated.
|
||||
// 2. Call x.stopListening()
|
||||
// 3. Call x.foo(). Problem: nothing happens (the original foo() method is not executed)
|
||||
stopListening(emitter, event, callback) {
|
||||
// Removing all listeners so let's clean the decorated methods to the original state.
|
||||
if (!emitter && this[decoratedMethods]) {
|
||||
for (const methodName of this[decoratedMethods]) {
|
||||
this[methodName] = this[methodName][decoratedOriginal];
|
||||
}
|
||||
delete this[decoratedMethods];
|
||||
}
|
||||
super.stopListening(emitter, event, callback);
|
||||
}
|
||||
}
|
||||
return Mixin;
|
||||
}
|
||||
// Backward compatibility with `mix`
|
||||
([
|
||||
'set', 'bind', 'unbind', 'decorate',
|
||||
'on', 'once', 'off', 'listenTo',
|
||||
'stopListening', 'fire', 'delegate', 'stopDelegating',
|
||||
'_addEventListener', '_removeEventListener'
|
||||
]).forEach(key => {
|
||||
ObservableMixin[key] = defaultObservableClass.prototype[key];
|
||||
});
|
||||
// Init symbol properties needed for the observable mechanism to work.
|
||||
function initObservable(observable) {
|
||||
// Do nothing if already inited.
|
||||
if (observable[observablePropertiesSymbol]) {
|
||||
return;
|
||||
}
|
||||
// The internal hash containing the observable's state.
|
||||
Object.defineProperty(observable, observablePropertiesSymbol, {
|
||||
value: new Map()
|
||||
});
|
||||
// Map containing bindings to external observables. It shares the binding objects
|
||||
// (`{ observable: A, property: 'a', to: ... }`) with {@link module:utils/observablemixin~Observable#_boundProperties} and
|
||||
// it is used to observe external observables to update own properties accordingly.
|
||||
// See {@link module:utils/observablemixin~Observable#bind}.
|
||||
//
|
||||
// A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
|
||||
// console.log( A._boundObservables );
|
||||
//
|
||||
// Map( {
|
||||
// B: {
|
||||
// x: Set( [
|
||||
// { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
|
||||
// { observable: A, property: 'c', to: [ [ B, 'x' ] ] }
|
||||
// ] ),
|
||||
// y: Set( [
|
||||
// { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
|
||||
// ] )
|
||||
// }
|
||||
// } )
|
||||
//
|
||||
// A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
|
||||
// console.log( A._boundObservables );
|
||||
//
|
||||
// Map( {
|
||||
// B: {
|
||||
// x: Set( [
|
||||
// { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
|
||||
// { observable: A, property: 'c', to: [ [ B, 'x' ] ] }
|
||||
// ] ),
|
||||
// y: Set( [
|
||||
// { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
|
||||
// ] ),
|
||||
// z: Set( [
|
||||
// { observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
|
||||
// ] )
|
||||
// },
|
||||
// C: {
|
||||
// w: Set( [
|
||||
// { observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
|
||||
// ] )
|
||||
// }
|
||||
// } )
|
||||
//
|
||||
Object.defineProperty(observable, boundObservablesSymbol, {
|
||||
value: new Map()
|
||||
});
|
||||
// Object that stores which properties of this observable are bound and how. It shares
|
||||
// the binding objects (`{ observable: A, property: 'a', to: ... }`) with
|
||||
// {@link module:utils/observablemixin~Observable#_boundObservables}. This data structure is
|
||||
// a reverse of {@link module:utils/observablemixin~Observable#_boundObservables} and it is helpful for
|
||||
// {@link module:utils/observablemixin~Observable#unbind}.
|
||||
//
|
||||
// See {@link module:utils/observablemixin~Observable#bind}.
|
||||
//
|
||||
// A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
|
||||
// console.log( A._boundProperties );
|
||||
//
|
||||
// Map( {
|
||||
// a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
|
||||
// b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
|
||||
// c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] }
|
||||
// } )
|
||||
//
|
||||
// A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
|
||||
// console.log( A._boundProperties );
|
||||
//
|
||||
// Map( {
|
||||
// a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
|
||||
// b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
|
||||
// c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] },
|
||||
// d: { observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
|
||||
// } )
|
||||
Object.defineProperty(observable, boundPropertiesSymbol, {
|
||||
value: new Map()
|
||||
});
|
||||
}
|
||||
/**
|
||||
* A chaining for {@link module:utils/observablemixin~Observable#bind} providing `.to()` interface.
|
||||
*
|
||||
* @param args Arguments of the `.to( args )` binding.
|
||||
*/
|
||||
function bindTo(...args) {
|
||||
const parsedArgs = parseBindToArgs(...args);
|
||||
const bindingsKeys = Array.from(this._bindings.keys());
|
||||
const numberOfBindings = bindingsKeys.length;
|
||||
// Eliminate A.bind( 'x' ).to( B, C )
|
||||
if (!parsedArgs.callback && parsedArgs.to.length > 1) {
|
||||
/**
|
||||
* Binding multiple observables only possible with callback.
|
||||
*
|
||||
* @error observable-bind-to-no-callback
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-to-no-callback', this);
|
||||
}
|
||||
// Eliminate A.bind( 'x', 'y' ).to( B, callback )
|
||||
if (numberOfBindings > 1 && parsedArgs.callback) {
|
||||
/**
|
||||
* Cannot bind multiple properties and use a callback in one binding.
|
||||
*
|
||||
* @error observable-bind-to-extra-callback
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-to-extra-callback', this);
|
||||
}
|
||||
parsedArgs.to.forEach(to => {
|
||||
// Eliminate A.bind( 'x', 'y' ).to( B, 'a' )
|
||||
if (to.properties.length && to.properties.length !== numberOfBindings) {
|
||||
/**
|
||||
* The number of properties must match.
|
||||
*
|
||||
* @error observable-bind-to-properties-length
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-to-properties-length', this);
|
||||
}
|
||||
// When no to.properties specified, observing source properties instead i.e.
|
||||
// A.bind( 'x', 'y' ).to( B ) -> Observe B.x and B.y
|
||||
if (!to.properties.length) {
|
||||
to.properties = this._bindProperties;
|
||||
}
|
||||
});
|
||||
this._to = parsedArgs.to;
|
||||
// Fill {@link BindChain#_bindings} with callback. When the callback is set there's only one binding.
|
||||
if (parsedArgs.callback) {
|
||||
this._bindings.get(bindingsKeys[0]).callback = parsedArgs.callback;
|
||||
}
|
||||
attachBindToListeners(this._observable, this._to);
|
||||
// Update observable._boundProperties and observable._boundObservables.
|
||||
updateBindToBound(this);
|
||||
// Set initial values of bound properties.
|
||||
this._bindProperties.forEach(propertyName => {
|
||||
updateBoundObservableProperty(this._observable, propertyName);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Binds to an attribute in a set of iterable observables.
|
||||
*/
|
||||
function bindToMany(observables, attribute, callback) {
|
||||
if (this._bindings.size > 1) {
|
||||
/**
|
||||
* Binding one attribute to many observables only possible with one attribute.
|
||||
*
|
||||
* @error observable-bind-to-many-not-one-binding
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-to-many-not-one-binding', this);
|
||||
}
|
||||
this.to(
|
||||
// Bind to #attribute of each observable...
|
||||
...getBindingTargets(observables, attribute),
|
||||
// ...using given callback to parse attribute values.
|
||||
callback);
|
||||
}
|
||||
/**
|
||||
* Returns an array of binding components for
|
||||
* {@link Observable#bind} from a set of iterable observables.
|
||||
*/
|
||||
function getBindingTargets(observables, attribute) {
|
||||
const observableAndAttributePairs = observables.map(observable => [observable, attribute]);
|
||||
// Merge pairs to one-dimension array of observables and attributes.
|
||||
return Array.prototype.concat.apply([], observableAndAttributePairs);
|
||||
}
|
||||
/**
|
||||
* Check if all entries of the array are of `String` type.
|
||||
*/
|
||||
function isStringArray(arr) {
|
||||
return arr.every(a => typeof a == 'string');
|
||||
}
|
||||
/**
|
||||
* Parses and validates {@link Observable#bind}`.to( args )` arguments and returns
|
||||
* an object with a parsed structure. For example
|
||||
*
|
||||
* ```ts
|
||||
* A.bind( 'x' ).to( B, 'a', C, 'b', call );
|
||||
* ```
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* to: [
|
||||
* { observable: B, properties: [ 'a' ] },
|
||||
* { observable: C, properties: [ 'b' ] },
|
||||
* ],
|
||||
* callback: call
|
||||
* }
|
||||
*
|
||||
* @param args Arguments of {@link Observable#bind}`.to( args )`.
|
||||
*/
|
||||
function parseBindToArgs(...args) {
|
||||
// Eliminate A.bind( 'x' ).to()
|
||||
if (!args.length) {
|
||||
/**
|
||||
* Invalid argument syntax in `to()`.
|
||||
*
|
||||
* @error observable-bind-to-parse-error
|
||||
*/
|
||||
throw new CKEditorError('observable-bind-to-parse-error', null);
|
||||
}
|
||||
const parsed = { to: [] };
|
||||
let lastObservable;
|
||||
if (typeof args[args.length - 1] == 'function') {
|
||||
parsed.callback = args.pop();
|
||||
}
|
||||
args.forEach(a => {
|
||||
if (typeof a == 'string') {
|
||||
lastObservable.properties.push(a);
|
||||
}
|
||||
else if (typeof a == 'object') {
|
||||
lastObservable = { observable: a, properties: [] };
|
||||
parsed.to.push(lastObservable);
|
||||
}
|
||||
else {
|
||||
throw new CKEditorError('observable-bind-to-parse-error', null);
|
||||
}
|
||||
});
|
||||
return parsed;
|
||||
}
|
||||
/**
|
||||
* Synchronizes {@link module:utils/observable#_boundObservables} with {@link Binding}.
|
||||
*
|
||||
* @param binding A binding to store in {@link Observable#_boundObservables}.
|
||||
* @param toObservable A observable, which is a new component of `binding`.
|
||||
* @param toPropertyName A name of `toObservable`'s property, a new component of the `binding`.
|
||||
*/
|
||||
function updateBoundObservables(observable, binding, toObservable, toPropertyName) {
|
||||
const boundObservables = observable[boundObservablesSymbol];
|
||||
const bindingsToObservable = boundObservables.get(toObservable);
|
||||
const bindings = bindingsToObservable || {};
|
||||
if (!bindings[toPropertyName]) {
|
||||
bindings[toPropertyName] = new Set();
|
||||
}
|
||||
// Pass the binding to a corresponding Set in `observable._boundObservables`.
|
||||
bindings[toPropertyName].add(binding);
|
||||
if (!bindingsToObservable) {
|
||||
boundObservables.set(toObservable, bindings);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Synchronizes {@link Observable#_boundProperties} and {@link Observable#_boundObservables}
|
||||
* with {@link BindChain}.
|
||||
*
|
||||
* Assuming the following binding being created
|
||||
*
|
||||
* ```ts
|
||||
* A.bind( 'a', 'b' ).to( B, 'x', 'y' );
|
||||
* ```
|
||||
*
|
||||
* the following bindings were initialized by {@link Observable#bind} in {@link BindChain#_bindings}:
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* a: { observable: A, property: 'a', to: [] },
|
||||
* b: { observable: A, property: 'b', to: [] },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Iterate over all bindings in this chain and fill their `to` properties with
|
||||
* corresponding to( ... ) arguments (components of the binding), so
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* a: { observable: A, property: 'a', to: [ B, 'x' ] },
|
||||
* b: { observable: A, property: 'b', to: [ B, 'y' ] },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then update the structure of {@link Observable#_boundObservables} with updated
|
||||
* binding, so it becomes:
|
||||
*
|
||||
* ```ts
|
||||
* Map( {
|
||||
* B: {
|
||||
* x: Set( [
|
||||
* { observable: A, property: 'a', to: [ [ B, 'x' ] ] }
|
||||
* ] ),
|
||||
* y: Set( [
|
||||
* { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
|
||||
* ] )
|
||||
* }
|
||||
* } )
|
||||
* ```
|
||||
*
|
||||
* @param chain The binding initialized by {@link Observable#bind}.
|
||||
*/
|
||||
function updateBindToBound(chain) {
|
||||
let toProperty;
|
||||
chain._bindings.forEach((binding, propertyName) => {
|
||||
// Note: For a binding without a callback, this will run only once
|
||||
// like in A.bind( 'x', 'y' ).to( B, 'a', 'b' )
|
||||
// TODO: ES6 destructuring.
|
||||
chain._to.forEach(to => {
|
||||
toProperty = to.properties[binding.callback ? 0 : chain._bindProperties.indexOf(propertyName)];
|
||||
binding.to.push([to.observable, toProperty]);
|
||||
updateBoundObservables(chain._observable, binding, to.observable, toProperty);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Updates an property of a {@link Observable} with a value
|
||||
* determined by an entry in {@link Observable#_boundProperties}.
|
||||
*
|
||||
* @param observable A observable which property is to be updated.
|
||||
* @param propertyName An property to be updated.
|
||||
*/
|
||||
function updateBoundObservableProperty(observable, propertyName) {
|
||||
const boundProperties = observable[boundPropertiesSymbol];
|
||||
const binding = boundProperties.get(propertyName);
|
||||
let propertyValue;
|
||||
// When a binding with callback is created like
|
||||
//
|
||||
// A.bind( 'a' ).to( B, 'b', C, 'c', callback );
|
||||
//
|
||||
// collect B.b and C.c, then pass them to callback to set A.a.
|
||||
if (binding.callback) {
|
||||
propertyValue = binding.callback.apply(observable, binding.to.map(to => to[0][to[1]]));
|
||||
}
|
||||
else {
|
||||
propertyValue = binding.to[0];
|
||||
propertyValue = propertyValue[0][propertyValue[1]];
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(observable, propertyName)) {
|
||||
observable[propertyName] = propertyValue;
|
||||
}
|
||||
else {
|
||||
observable.set(propertyName, propertyValue);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Starts listening to changes in {@link BindChain._to} observables to update
|
||||
* {@link BindChain._observable} {@link BindChain._bindProperties}. Also sets the
|
||||
* initial state of {@link BindChain._observable}.
|
||||
*
|
||||
* @param chain The chain initialized by {@link Observable#bind}.
|
||||
*/
|
||||
function attachBindToListeners(observable, toBindings) {
|
||||
toBindings.forEach(to => {
|
||||
const boundObservables = observable[boundObservablesSymbol];
|
||||
let bindings;
|
||||
// If there's already a chain between the observables (`observable` listens to
|
||||
// `to.observable`), there's no need to create another `change` event listener.
|
||||
if (!boundObservables.get(to.observable)) {
|
||||
observable.listenTo(to.observable, 'change', (evt, propertyName) => {
|
||||
bindings = boundObservables.get(to.observable)[propertyName];
|
||||
// Note: to.observable will fire for any property change, react
|
||||
// to changes of properties which are bound only.
|
||||
if (bindings) {
|
||||
bindings.forEach(binding => {
|
||||
updateBoundObservableProperty(observable, binding.property);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
33
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/priorities.d.ts
generated
vendored
Normal file
33
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/priorities.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @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/priorities
|
||||
*/
|
||||
/**
|
||||
* String representing a priority value.
|
||||
*/
|
||||
export type PriorityString = 'highest' | 'high' | 'normal' | 'low' | 'lowest' | number;
|
||||
/**
|
||||
* Provides group of constants to use instead of hardcoding numeric priority values.
|
||||
*/
|
||||
export interface PrioritiesType {
|
||||
/**
|
||||
* Converts a string with priority name to it's numeric value. If `Number` is given, it just returns it.
|
||||
*
|
||||
* @param priority Priority to convert.
|
||||
* @returns Converted priority.
|
||||
*/
|
||||
get(priority?: PriorityString): number;
|
||||
readonly highest: number;
|
||||
readonly high: number;
|
||||
readonly normal: number;
|
||||
readonly low: number;
|
||||
readonly lowest: number;
|
||||
}
|
||||
/**
|
||||
* Provides group of constants to use instead of hardcoding numeric priority values.
|
||||
*/
|
||||
declare const priorities: PrioritiesType;
|
||||
export default priorities;
|
||||
23
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/priorities.js
generated
vendored
Normal file
23
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/priorities.js
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Provides group of constants to use instead of hardcoding numeric priority values.
|
||||
*/
|
||||
const priorities = {
|
||||
get(priority = 'normal') {
|
||||
if (typeof priority != 'number') {
|
||||
return this[priority] || this.normal;
|
||||
}
|
||||
else {
|
||||
return priority;
|
||||
}
|
||||
},
|
||||
highest: 100000,
|
||||
high: 1000,
|
||||
normal: 0,
|
||||
low: -1000,
|
||||
lowest: -100000
|
||||
};
|
||||
export default priorities;
|
||||
33
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/retry.d.ts
generated
vendored
Normal file
33
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/retry.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Tries calling the given callback until it sucessfully resolves.
|
||||
*
|
||||
* If the callback fails `maxRetries` times, the returned promise is rejected with the last error.
|
||||
*
|
||||
* @typeParam TResult The result of a successful callback invocation.
|
||||
* @param callback The function to call until it succeeds.
|
||||
* @param options.maxRetries Maximum number of retries.
|
||||
* @param options.retryDelay The time in milliseconds between attempts. By default it implements exponential back-off policy.
|
||||
* @param options.signal The signal to abort further retries. The callback itself is not aborted automatically.
|
||||
*/
|
||||
export default function retry<TResult>(callback: () => Promise<TResult>, options?: {
|
||||
maxAttempts?: number;
|
||||
retryDelay?: (attempt: number) => number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<TResult>;
|
||||
/**
|
||||
* Creates a function that calculates exponential back-off delay. Pass it as `options.retryDelay` to {@link ~retry}.
|
||||
*
|
||||
* @param options.delay Base delay between invocations. Defaults to 1s.
|
||||
* @param options.factor How much to increase the delay. Defaults to 2x.
|
||||
* @param options.maxDelay Maximum timeout. Even if higher timeout is calculated, it cannot get higher than this value. Default to 10s.
|
||||
* @returns The function calculating the delay.
|
||||
*/
|
||||
export declare function exponentialDelay(options?: {
|
||||
delay?: number;
|
||||
factor?: number;
|
||||
maxDelay?: number;
|
||||
}): (attempt: number) => number;
|
||||
47
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/retry.js
generated
vendored
Normal file
47
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/retry.js
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* @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/retry
|
||||
*/
|
||||
import wait from './wait.js';
|
||||
/**
|
||||
* Tries calling the given callback until it sucessfully resolves.
|
||||
*
|
||||
* If the callback fails `maxRetries` times, the returned promise is rejected with the last error.
|
||||
*
|
||||
* @typeParam TResult The result of a successful callback invocation.
|
||||
* @param callback The function to call until it succeeds.
|
||||
* @param options.maxRetries Maximum number of retries.
|
||||
* @param options.retryDelay The time in milliseconds between attempts. By default it implements exponential back-off policy.
|
||||
* @param options.signal The signal to abort further retries. The callback itself is not aborted automatically.
|
||||
*/
|
||||
export default async function retry(callback, options = {}) {
|
||||
const { maxAttempts = 4, retryDelay = exponentialDelay(), signal = (new AbortController()).signal } = options;
|
||||
signal.throwIfAborted();
|
||||
for (let attempt = 0;; attempt++) {
|
||||
try {
|
||||
return await callback();
|
||||
}
|
||||
catch (err) {
|
||||
const isLast = attempt + 1 >= maxAttempts;
|
||||
if (isLast) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
await wait(retryDelay(attempt), { signal });
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a function that calculates exponential back-off delay. Pass it as `options.retryDelay` to {@link ~retry}.
|
||||
*
|
||||
* @param options.delay Base delay between invocations. Defaults to 1s.
|
||||
* @param options.factor How much to increase the delay. Defaults to 2x.
|
||||
* @param options.maxDelay Maximum timeout. Even if higher timeout is calculated, it cannot get higher than this value. Default to 10s.
|
||||
* @returns The function calculating the delay.
|
||||
*/
|
||||
export function exponentialDelay(options = {}) {
|
||||
const { delay = 1000, factor = 2, maxDelay = 10000 } = options;
|
||||
return attempt => Math.min(factor ** attempt * delay, maxDelay);
|
||||
}
|
||||
26
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/splicearray.d.ts
generated
vendored
Normal file
26
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/splicearray.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Splices one array into another. To be used instead of `Array.prototype.splice` as the latter may
|
||||
* throw "Maximum call stack size exceeded" when passed huge number of items to insert.
|
||||
*
|
||||
* Note: in contrary to Array.splice, this function does not modify the original `target`.
|
||||
*
|
||||
* ```ts
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 0, 0 ); // [ 3, 4, 1, 2 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 1 ); // [ 1, 3, 4 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 0 ); // [ 1, 3, 4, 2 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 2, 0 ); // [ 1, 2, 3, 4 ]
|
||||
* spliceArray( [ 1, 2 ], [], 0, 1 ); // [ 2 ]
|
||||
* ```
|
||||
*
|
||||
* @param target Array to be spliced.
|
||||
* @param source Array of elements to be inserted to target.
|
||||
* @param start Index at which nodes should be inserted/removed.
|
||||
* @param count Number of items.
|
||||
*
|
||||
* @returns New spliced array.
|
||||
*/
|
||||
export default function spliceArray<T>(target: ReadonlyArray<T>, source: ReadonlyArray<T>, start: number, count: number): Array<T>;
|
||||
40
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/splicearray.js
generated
vendored
Normal file
40
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/splicearray.js
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* @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/splicearray
|
||||
*/
|
||||
const BIG_CHUNK_SIZE = 10000;
|
||||
/**
|
||||
* Splices one array into another. To be used instead of `Array.prototype.splice` as the latter may
|
||||
* throw "Maximum call stack size exceeded" when passed huge number of items to insert.
|
||||
*
|
||||
* Note: in contrary to Array.splice, this function does not modify the original `target`.
|
||||
*
|
||||
* ```ts
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 0, 0 ); // [ 3, 4, 1, 2 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 1 ); // [ 1, 3, 4 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 0 ); // [ 1, 3, 4, 2 ]
|
||||
* spliceArray( [ 1, 2 ], [ 3, 4 ], 2, 0 ); // [ 1, 2, 3, 4 ]
|
||||
* spliceArray( [ 1, 2 ], [], 0, 1 ); // [ 2 ]
|
||||
* ```
|
||||
*
|
||||
* @param target Array to be spliced.
|
||||
* @param source Array of elements to be inserted to target.
|
||||
* @param start Index at which nodes should be inserted/removed.
|
||||
* @param count Number of items.
|
||||
*
|
||||
* @returns New spliced array.
|
||||
*/
|
||||
export default function spliceArray(target, source, start, count) {
|
||||
// In case of performance problems, see: https://github.com/ckeditor/ckeditor5/pull/12429/files#r965850568
|
||||
if (Math.max(source.length, target.length) > BIG_CHUNK_SIZE) {
|
||||
return target.slice(0, start).concat(source).concat(target.slice(start + count, target.length));
|
||||
}
|
||||
else {
|
||||
const newTarget = Array.from(target);
|
||||
newTarget.splice(start, count, ...source);
|
||||
return newTarget;
|
||||
}
|
||||
}
|
||||
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/spy.d.ts
generated
vendored
Normal file
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/spy.d.ts
generated
vendored
Normal file
|
|
@ -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/spy
|
||||
*/
|
||||
/**
|
||||
* Creates a spy function (ala Sinon.js) that can be used to inspect call to it.
|
||||
*
|
||||
* The following are the present features:
|
||||
*
|
||||
* * spy.called: property set to `true` if the function has been called at least once.
|
||||
*
|
||||
* @returns The spy function.
|
||||
*/
|
||||
declare function spy(): {
|
||||
(): void;
|
||||
called?: boolean;
|
||||
};
|
||||
export default spy;
|
||||
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/spy.js
generated
vendored
Normal file
22
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/spy.js
generated
vendored
Normal file
|
|
@ -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/spy
|
||||
*/
|
||||
/**
|
||||
* Creates a spy function (ala Sinon.js) that can be used to inspect call to it.
|
||||
*
|
||||
* The following are the present features:
|
||||
*
|
||||
* * spy.called: property set to `true` if the function has been called at least once.
|
||||
*
|
||||
* @returns The spy function.
|
||||
*/
|
||||
function spy() {
|
||||
return function spy() {
|
||||
spy.called = true;
|
||||
};
|
||||
}
|
||||
export default spy;
|
||||
25
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/toarray.d.ts
generated
vendored
Normal file
25
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/toarray.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* @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/toarray
|
||||
*/
|
||||
/**
|
||||
* Transforms any value to an array. If the provided value is already an array, it is returned unchanged.
|
||||
*
|
||||
* @label MUTABLE
|
||||
* @param data The value to transform to an array.
|
||||
* @returns An array created from data.
|
||||
*/
|
||||
export default function toArray<T>(data: ArrayOrItem<T>): Array<T>;
|
||||
/**
|
||||
* Transforms any value to an array. If the provided value is already an array, it is returned unchanged.
|
||||
*
|
||||
* @label IMMUTABLE
|
||||
* @param data The value to transform to an array.
|
||||
* @returns An array created from data.
|
||||
*/
|
||||
export default function toArray<T>(data: ReadonlyArrayOrItem<T>): ReadonlyArray<T>;
|
||||
export type ArrayOrItem<T> = T | Array<T>;
|
||||
export type ReadonlyArrayOrItem<T> = T | ReadonlyArray<T>;
|
||||
7
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/toarray.js
generated
vendored
Normal file
7
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/toarray.js
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
export default function toArray(data) {
|
||||
return Array.isArray(data) ? data : [data];
|
||||
}
|
||||
19
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/tomap.d.ts
generated
vendored
Normal file
19
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/tomap.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Transforms object or iterable to map. Iterable needs to be in the format acceptable by the `Map` constructor.
|
||||
*
|
||||
* ```ts
|
||||
* map = toMap( { 'foo': 1, 'bar': 2 } );
|
||||
* map = toMap( [ [ 'foo', 1 ], [ 'bar', 2 ] ] );
|
||||
* map = toMap( anotherMap );
|
||||
* ```
|
||||
*
|
||||
* @param data Object or iterable to transform.
|
||||
* @returns Map created from data.
|
||||
*/
|
||||
export default function toMap<T>(data: {
|
||||
readonly [key: string]: T;
|
||||
} | Iterable<readonly [string, T]> | null | undefined): Map<string, T>;
|
||||
29
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/tomap.js
generated
vendored
Normal file
29
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/tomap.js
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @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/tomap
|
||||
*/
|
||||
import objectToMap from './objecttomap.js';
|
||||
import isIterable from './isiterable.js';
|
||||
/**
|
||||
* Transforms object or iterable to map. Iterable needs to be in the format acceptable by the `Map` constructor.
|
||||
*
|
||||
* ```ts
|
||||
* map = toMap( { 'foo': 1, 'bar': 2 } );
|
||||
* map = toMap( [ [ 'foo', 1 ], [ 'bar', 2 ] ] );
|
||||
* map = toMap( anotherMap );
|
||||
* ```
|
||||
*
|
||||
* @param data Object or iterable to transform.
|
||||
* @returns Map created from data.
|
||||
*/
|
||||
export default function toMap(data) {
|
||||
if (isIterable(data)) {
|
||||
return new Map(data);
|
||||
}
|
||||
else {
|
||||
return objectToMap(data);
|
||||
}
|
||||
}
|
||||
174
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/translation-service.d.ts
generated
vendored
Normal file
174
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/translation-service.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
* @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/translation-service
|
||||
*/
|
||||
import type { Translations } from '@ckeditor/ckeditor5-utils/src/locale.js';
|
||||
import { type ArrayOrItem } from '@ckeditor/ckeditor5-utils/src/toarray.js';
|
||||
declare global {
|
||||
var CKEDITOR_TRANSLATIONS: Translations;
|
||||
}
|
||||
/**
|
||||
* Adds translations to existing ones or overrides the existing translations. These translations will later
|
||||
* be available for the {@link module:utils/locale~Locale#t `t()`} function.
|
||||
*
|
||||
* The `translations` is an object which consists of `messageId: translation` pairs. Note that the message ID can be
|
||||
* either constructed from the message string or from the message ID if it was passed
|
||||
* (this happens rarely and mostly for short messages or messages with placeholders).
|
||||
* Since the editor displays only the message string, the message ID can be found either in the source code or in the
|
||||
* built translations for another language.
|
||||
*
|
||||
* ```ts
|
||||
* add( 'pl', {
|
||||
* 'Cancel': 'Anuluj',
|
||||
* 'IMAGE': 'obraz', // Note that the `IMAGE` comes from the message ID, while the string can be `image`.
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* If the message is supposed to support various plural forms, make sure to provide an array with the singular form and all plural forms:
|
||||
*
|
||||
* ```ts
|
||||
* add( 'pl', {
|
||||
* 'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* You should also specify the third argument (the `getPluralForm()` function) that will be used to determine the plural form if no
|
||||
* language file was loaded for that language. All language files coming from CKEditor 5 sources will have this option set, so
|
||||
* these plural form rules will be reused by other translations added to the registered languages. The `getPluralForm()` function
|
||||
* can return either a Boolean or a number.
|
||||
*
|
||||
* ```ts
|
||||
* add( 'en', {
|
||||
* // ... Translations.
|
||||
* }, n => n !== 1 );
|
||||
* add( 'pl', {
|
||||
* // ... Translations.
|
||||
* }, n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 );
|
||||
* ```
|
||||
*
|
||||
* All translations extend the global `window.CKEDITOR_TRANSLATIONS` object. An example of this object can be found below:
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* pl: {
|
||||
* dictionary: {
|
||||
* 'Cancel': 'Anuluj',
|
||||
* 'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
|
||||
* },
|
||||
* // A function that returns the plural form index.
|
||||
* getPluralForm: n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 );
|
||||
* }
|
||||
* // Other languages.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* If you cannot import this function from this module (e.g. because you use a CKEditor 5 build), you can
|
||||
* still add translations by extending the global `window.CKEDITOR_TRANSLATIONS` object by using a function like
|
||||
* the one below:
|
||||
*
|
||||
* ```ts
|
||||
* function addTranslations( language, translations, getPluralForm ) {
|
||||
* if ( !global.window.CKEDITOR_TRANSLATIONS ) {
|
||||
* global.window.CKEDITOR_TRANSLATIONS = {};
|
||||
* }
|
||||
|
||||
* if ( !global.window.CKEDITOR_TRANSLATIONS[ language ] ) {
|
||||
* global.window.CKEDITOR_TRANSLATIONS[ language ] = {};
|
||||
* }
|
||||
*
|
||||
* const languageTranslations = global.window.CKEDITOR_TRANSLATIONS[ language ];
|
||||
*
|
||||
* languageTranslations.dictionary = languageTranslations.dictionary || {};
|
||||
* languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm;
|
||||
*
|
||||
* // Extend the dictionary for the given language.
|
||||
* Object.assign( languageTranslations.dictionary, translations );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param language Target language.
|
||||
* @param translations An object with translations which will be added to the dictionary.
|
||||
* For each message ID the value should be either a translation or an array of translations if the message
|
||||
* should support plural forms.
|
||||
* @param getPluralForm A function that returns the plural form index (a number).
|
||||
*/
|
||||
export declare function add(language: string, translations: {
|
||||
readonly [messageId: string]: string | ReadonlyArray<string>;
|
||||
}, getPluralForm?: (n: number) => number): void;
|
||||
/**
|
||||
* **Note:** This method is internal, use {@link module:utils/locale~Locale#t the `t()` function} instead to translate
|
||||
* the editor UI parts.
|
||||
*
|
||||
* This function is responsible for translating messages to the specified language. It uses translations added perviously
|
||||
* by {@link module:utils/translation-service~add} (a translations dictionary and the `getPluralForm()` function
|
||||
* to provide accurate translations of plural forms).
|
||||
*
|
||||
* When no translation is defined in the dictionary or the dictionary does not exist, this function returns
|
||||
* the original message string or the message plural depending on the number of elements.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'pl', { string: 'Cancel' } ); // 'Cancel'
|
||||
* ```
|
||||
*
|
||||
* The third optional argument is the number of elements, based on which the single form or one of the plural forms
|
||||
* should be picked when the message is supposed to support various plural forms.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Add a space'
|
||||
* translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 3 ); // 'Add %0 spaces'
|
||||
* ```
|
||||
*
|
||||
* The message should provide an ID using the `id` property when the message strings are not unique and their
|
||||
* translations should be different.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'en', { string: 'image', id: 'ADD_IMAGE' } );
|
||||
* translate( 'en', { string: 'image', id: 'AN_IMAGE' } );
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
* @param language Target language.
|
||||
* @param message A message that will be translated.
|
||||
* @param quantity The number of elements for which a plural form should be picked from the target language dictionary.
|
||||
* @param translations Translations passed in editor config, if not provided use the global `window.CKEDITOR_TRANSLATIONS`.
|
||||
* @returns Translated sentence.
|
||||
*/
|
||||
export declare function _translate(language: string, message: Message, quantity?: number, translations?: Translations): string;
|
||||
/**
|
||||
* Clears dictionaries for test purposes.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare function _clear(): void;
|
||||
/**
|
||||
* If array then merge objects which are inside otherwise return given object.
|
||||
*
|
||||
* @internal
|
||||
* @param translations Translations passed in editor config.
|
||||
*/
|
||||
export declare function _unifyTranslations(translations?: ArrayOrItem<Translations>): Translations | undefined;
|
||||
/**
|
||||
* The internationalization message interface. A message that implements this interface can be passed to the `t()` function
|
||||
* to be translated to the target UI language.
|
||||
*/
|
||||
export interface Message {
|
||||
/**
|
||||
* The message string to translate. Acts as a default translation if the translation for a given language
|
||||
* is not defined. When the message is supposed to support plural forms, the string should be the English singular form of the message.
|
||||
*/
|
||||
readonly string: string;
|
||||
/**
|
||||
* The message ID. If passed, the message ID is taken from this property instead of the `message.string`.
|
||||
* This property is useful when various messages share the same message string, for example, the `editor` string in `in the editor`
|
||||
* and `my editor` sentences.
|
||||
*/
|
||||
readonly id?: string;
|
||||
/**
|
||||
* The plural form of the message. This property should be skipped when a message is not supposed
|
||||
* to support plural forms. Otherwise it should always be set to a string with the English plural form of the message.
|
||||
*/
|
||||
readonly plural?: string;
|
||||
}
|
||||
209
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/translation-service.js
generated
vendored
Normal file
209
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/translation-service.js
generated
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/**
|
||||
* @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 CKEditorError from './ckeditorerror.js';
|
||||
import global from './dom/global.js';
|
||||
import { merge } from 'lodash-es';
|
||||
/* istanbul ignore else -- @preserve */
|
||||
if (!global.window.CKEDITOR_TRANSLATIONS) {
|
||||
global.window.CKEDITOR_TRANSLATIONS = {};
|
||||
}
|
||||
/**
|
||||
* Adds translations to existing ones or overrides the existing translations. These translations will later
|
||||
* be available for the {@link module:utils/locale~Locale#t `t()`} function.
|
||||
*
|
||||
* The `translations` is an object which consists of `messageId: translation` pairs. Note that the message ID can be
|
||||
* either constructed from the message string or from the message ID if it was passed
|
||||
* (this happens rarely and mostly for short messages or messages with placeholders).
|
||||
* Since the editor displays only the message string, the message ID can be found either in the source code or in the
|
||||
* built translations for another language.
|
||||
*
|
||||
* ```ts
|
||||
* add( 'pl', {
|
||||
* 'Cancel': 'Anuluj',
|
||||
* 'IMAGE': 'obraz', // Note that the `IMAGE` comes from the message ID, while the string can be `image`.
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* If the message is supposed to support various plural forms, make sure to provide an array with the singular form and all plural forms:
|
||||
*
|
||||
* ```ts
|
||||
* add( 'pl', {
|
||||
* 'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* You should also specify the third argument (the `getPluralForm()` function) that will be used to determine the plural form if no
|
||||
* language file was loaded for that language. All language files coming from CKEditor 5 sources will have this option set, so
|
||||
* these plural form rules will be reused by other translations added to the registered languages. The `getPluralForm()` function
|
||||
* can return either a Boolean or a number.
|
||||
*
|
||||
* ```ts
|
||||
* add( 'en', {
|
||||
* // ... Translations.
|
||||
* }, n => n !== 1 );
|
||||
* add( 'pl', {
|
||||
* // ... Translations.
|
||||
* }, n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 );
|
||||
* ```
|
||||
*
|
||||
* All translations extend the global `window.CKEDITOR_TRANSLATIONS` object. An example of this object can be found below:
|
||||
*
|
||||
* ```ts
|
||||
* {
|
||||
* pl: {
|
||||
* dictionary: {
|
||||
* 'Cancel': 'Anuluj',
|
||||
* 'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
|
||||
* },
|
||||
* // A function that returns the plural form index.
|
||||
* getPluralForm: n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 );
|
||||
* }
|
||||
* // Other languages.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* If you cannot import this function from this module (e.g. because you use a CKEditor 5 build), you can
|
||||
* still add translations by extending the global `window.CKEDITOR_TRANSLATIONS` object by using a function like
|
||||
* the one below:
|
||||
*
|
||||
* ```ts
|
||||
* function addTranslations( language, translations, getPluralForm ) {
|
||||
* if ( !global.window.CKEDITOR_TRANSLATIONS ) {
|
||||
* global.window.CKEDITOR_TRANSLATIONS = {};
|
||||
* }
|
||||
|
||||
* if ( !global.window.CKEDITOR_TRANSLATIONS[ language ] ) {
|
||||
* global.window.CKEDITOR_TRANSLATIONS[ language ] = {};
|
||||
* }
|
||||
*
|
||||
* const languageTranslations = global.window.CKEDITOR_TRANSLATIONS[ language ];
|
||||
*
|
||||
* languageTranslations.dictionary = languageTranslations.dictionary || {};
|
||||
* languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm;
|
||||
*
|
||||
* // Extend the dictionary for the given language.
|
||||
* Object.assign( languageTranslations.dictionary, translations );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param language Target language.
|
||||
* @param translations An object with translations which will be added to the dictionary.
|
||||
* For each message ID the value should be either a translation or an array of translations if the message
|
||||
* should support plural forms.
|
||||
* @param getPluralForm A function that returns the plural form index (a number).
|
||||
*/
|
||||
export function add(language, translations, getPluralForm) {
|
||||
if (!global.window.CKEDITOR_TRANSLATIONS[language]) {
|
||||
global.window.CKEDITOR_TRANSLATIONS[language] = {};
|
||||
}
|
||||
const languageTranslations = global.window.CKEDITOR_TRANSLATIONS[language];
|
||||
languageTranslations.dictionary = languageTranslations.dictionary || {};
|
||||
languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm;
|
||||
Object.assign(languageTranslations.dictionary, translations);
|
||||
}
|
||||
/**
|
||||
* **Note:** This method is internal, use {@link module:utils/locale~Locale#t the `t()` function} instead to translate
|
||||
* the editor UI parts.
|
||||
*
|
||||
* This function is responsible for translating messages to the specified language. It uses translations added perviously
|
||||
* by {@link module:utils/translation-service~add} (a translations dictionary and the `getPluralForm()` function
|
||||
* to provide accurate translations of plural forms).
|
||||
*
|
||||
* When no translation is defined in the dictionary or the dictionary does not exist, this function returns
|
||||
* the original message string or the message plural depending on the number of elements.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'pl', { string: 'Cancel' } ); // 'Cancel'
|
||||
* ```
|
||||
*
|
||||
* The third optional argument is the number of elements, based on which the single form or one of the plural forms
|
||||
* should be picked when the message is supposed to support various plural forms.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Add a space'
|
||||
* translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 3 ); // 'Add %0 spaces'
|
||||
* ```
|
||||
*
|
||||
* The message should provide an ID using the `id` property when the message strings are not unique and their
|
||||
* translations should be different.
|
||||
*
|
||||
* ```ts
|
||||
* translate( 'en', { string: 'image', id: 'ADD_IMAGE' } );
|
||||
* translate( 'en', { string: 'image', id: 'AN_IMAGE' } );
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
* @param language Target language.
|
||||
* @param message A message that will be translated.
|
||||
* @param quantity The number of elements for which a plural form should be picked from the target language dictionary.
|
||||
* @param translations Translations passed in editor config, if not provided use the global `window.CKEDITOR_TRANSLATIONS`.
|
||||
* @returns Translated sentence.
|
||||
*/
|
||||
export function _translate(language, message, quantity = 1, translations) {
|
||||
if (typeof quantity !== 'number') {
|
||||
/**
|
||||
* An incorrect value was passed to the translation function. This was probably caused
|
||||
* by an incorrect message interpolation of a plural form. Note that for messages supporting plural forms
|
||||
* the second argument of the `t()` function should always be a number or an array with a number as the first element.
|
||||
*
|
||||
* @error translation-service-quantity-not-a-number
|
||||
*/
|
||||
throw new CKEditorError('translation-service-quantity-not-a-number', null, { quantity });
|
||||
}
|
||||
const normalizedTranslations = translations || global.window.CKEDITOR_TRANSLATIONS;
|
||||
const numberOfLanguages = getNumberOfLanguages(normalizedTranslations);
|
||||
if (numberOfLanguages === 1) {
|
||||
// Override the language to the only supported one.
|
||||
// This can't be done in the `Locale` class, because the translations comes after the `Locale` class initialization.
|
||||
language = Object.keys(normalizedTranslations)[0];
|
||||
}
|
||||
const messageId = message.id || message.string;
|
||||
if (numberOfLanguages === 0 || !hasTranslation(language, messageId, normalizedTranslations)) {
|
||||
if (quantity !== 1) {
|
||||
// Return the default plural form that was passed in the `message.plural` parameter.
|
||||
return message.plural;
|
||||
}
|
||||
return message.string;
|
||||
}
|
||||
const dictionary = normalizedTranslations[language].dictionary;
|
||||
const getPluralForm = normalizedTranslations[language].getPluralForm || (n => n === 1 ? 0 : 1);
|
||||
const translation = dictionary[messageId];
|
||||
if (typeof translation === 'string') {
|
||||
return translation;
|
||||
}
|
||||
const pluralFormIndex = Number(getPluralForm(quantity));
|
||||
// Note: The `translate` function is not responsible for replacing `%0, %1, ...` with values.
|
||||
return translation[pluralFormIndex];
|
||||
}
|
||||
/**
|
||||
* Clears dictionaries for test purposes.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function _clear() {
|
||||
if (global.window.CKEDITOR_TRANSLATIONS) {
|
||||
global.window.CKEDITOR_TRANSLATIONS = {};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If array then merge objects which are inside otherwise return given object.
|
||||
*
|
||||
* @internal
|
||||
* @param translations Translations passed in editor config.
|
||||
*/
|
||||
export function _unifyTranslations(translations) {
|
||||
return Array.isArray(translations) ?
|
||||
translations.reduce((acc, translation) => merge(acc, translation)) :
|
||||
translations;
|
||||
}
|
||||
/**
|
||||
* Checks whether the dictionary exists and translation in that dictionary exists.
|
||||
*/
|
||||
function hasTranslation(language, messageId, translations) {
|
||||
return !!translations[language] && !!translations[language].dictionary[messageId];
|
||||
}
|
||||
function getNumberOfLanguages(translations) {
|
||||
return Object.keys(translations).length;
|
||||
}
|
||||
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/uid.d.ts
generated
vendored
Normal file
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/uid.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Returns a unique id. The id starts with an "e" character and a randomly generated string of
|
||||
* 32 alphanumeric characters.
|
||||
*
|
||||
* **Note**: The characters the unique id is built from correspond to the hex number notation
|
||||
* (from "0" to "9", from "a" to "f"). In other words, each id corresponds to an "e" followed
|
||||
* by 16 8-bit numbers next to each other.
|
||||
*
|
||||
* @returns An unique id string.
|
||||
*/
|
||||
export default function uid(): string;
|
||||
57
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/uid.js
generated
vendored
Normal file
57
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/uid.js
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @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/uid
|
||||
*/
|
||||
/**
|
||||
* A hash table of hex numbers to avoid using toString() in uid() which is costly.
|
||||
* [ '00', '01', '02', ..., 'fe', 'ff' ]
|
||||
*/
|
||||
const HEX_NUMBERS = new Array(256).fill('')
|
||||
.map((_, index) => ('0' + (index).toString(16)).slice(-2));
|
||||
/**
|
||||
* Returns a unique id. The id starts with an "e" character and a randomly generated string of
|
||||
* 32 alphanumeric characters.
|
||||
*
|
||||
* **Note**: The characters the unique id is built from correspond to the hex number notation
|
||||
* (from "0" to "9", from "a" to "f"). In other words, each id corresponds to an "e" followed
|
||||
* by 16 8-bit numbers next to each other.
|
||||
*
|
||||
* @returns An unique id string.
|
||||
*/
|
||||
export default function uid() {
|
||||
// Let's create some positive random 32bit integers first.
|
||||
//
|
||||
// 1. Math.random() is a float between 0 and 1.
|
||||
// 2. 0x100000000 is 2^32 = 4294967296.
|
||||
// 3. >>> 0 enforces integer (in JS all numbers are floating point).
|
||||
//
|
||||
// For instance:
|
||||
// Math.random() * 0x100000000 = 3366450031.853859
|
||||
// but
|
||||
// Math.random() * 0x100000000 >>> 0 = 3366450031.
|
||||
const r1 = Math.random() * 0x100000000 >>> 0;
|
||||
const r2 = Math.random() * 0x100000000 >>> 0;
|
||||
const r3 = Math.random() * 0x100000000 >>> 0;
|
||||
const r4 = Math.random() * 0x100000000 >>> 0;
|
||||
// Make sure that id does not start with number.
|
||||
return 'e' +
|
||||
HEX_NUMBERS[r1 >> 0 & 0xFF] +
|
||||
HEX_NUMBERS[r1 >> 8 & 0xFF] +
|
||||
HEX_NUMBERS[r1 >> 16 & 0xFF] +
|
||||
HEX_NUMBERS[r1 >> 24 & 0xFF] +
|
||||
HEX_NUMBERS[r2 >> 0 & 0xFF] +
|
||||
HEX_NUMBERS[r2 >> 8 & 0xFF] +
|
||||
HEX_NUMBERS[r2 >> 16 & 0xFF] +
|
||||
HEX_NUMBERS[r2 >> 24 & 0xFF] +
|
||||
HEX_NUMBERS[r3 >> 0 & 0xFF] +
|
||||
HEX_NUMBERS[r3 >> 8 & 0xFF] +
|
||||
HEX_NUMBERS[r3 >> 16 & 0xFF] +
|
||||
HEX_NUMBERS[r3 >> 24 & 0xFF] +
|
||||
HEX_NUMBERS[r4 >> 0 & 0xFF] +
|
||||
HEX_NUMBERS[r4 >> 8 & 0xFF] +
|
||||
HEX_NUMBERS[r4 >> 16 & 0xFF] +
|
||||
HEX_NUMBERS[r4 >> 24 & 0xFF];
|
||||
}
|
||||
54
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/unicode.d.ts
generated
vendored
Normal file
54
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/unicode.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Set of utils to handle unicode characters.
|
||||
*
|
||||
* @module utils/unicode
|
||||
*/
|
||||
/**
|
||||
* Checks whether given `character` is a combining mark.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export declare function isCombiningMark(character: string): boolean;
|
||||
/**
|
||||
* Checks whether given `character` is a high half of surrogate pair.
|
||||
*
|
||||
* Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
|
||||
* consist of high surrogate pair character followed by low surrogate pair character.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export declare function isHighSurrogateHalf(character: string): boolean;
|
||||
/**
|
||||
* Checks whether given `character` is a low half of surrogate pair.
|
||||
*
|
||||
* Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
|
||||
* consist of high surrogate pair character followed by low surrogate pair character.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export declare function isLowSurrogateHalf(character: string): boolean;
|
||||
/**
|
||||
* Checks whether given offset in a string is inside a surrogate pair (between two surrogate halves).
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export declare function isInsideSurrogatePair(string: string, offset: number): boolean;
|
||||
/**
|
||||
* Checks whether given offset in a string is between base character and combining mark or between two combining marks.
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export declare function isInsideCombinedSymbol(string: string, offset: number): boolean;
|
||||
/**
|
||||
* Checks whether given offset in a string is inside multi-character emoji sequence.
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export declare function isInsideEmojiSequence(string: string, offset: number): boolean;
|
||||
85
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/unicode.js
generated
vendored
Normal file
85
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/unicode.js
generated
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Set of utils to handle unicode characters.
|
||||
*
|
||||
* @module utils/unicode
|
||||
*/
|
||||
/**
|
||||
* Checks whether given `character` is a combining mark.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export function isCombiningMark(character) {
|
||||
// eslint-disable-next-line no-misleading-character-class
|
||||
return !!character && character.length == 1 && /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/.test(character);
|
||||
}
|
||||
/**
|
||||
* Checks whether given `character` is a high half of surrogate pair.
|
||||
*
|
||||
* Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
|
||||
* consist of high surrogate pair character followed by low surrogate pair character.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export function isHighSurrogateHalf(character) {
|
||||
return !!character && character.length == 1 && /[\ud800-\udbff]/.test(character);
|
||||
}
|
||||
/**
|
||||
* Checks whether given `character` is a low half of surrogate pair.
|
||||
*
|
||||
* Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
|
||||
* consist of high surrogate pair character followed by low surrogate pair character.
|
||||
*
|
||||
* @param character Character to check.
|
||||
*/
|
||||
export function isLowSurrogateHalf(character) {
|
||||
return !!character && character.length == 1 && /[\udc00-\udfff]/.test(character);
|
||||
}
|
||||
/**
|
||||
* Checks whether given offset in a string is inside a surrogate pair (between two surrogate halves).
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export function isInsideSurrogatePair(string, offset) {
|
||||
return isHighSurrogateHalf(string.charAt(offset - 1)) && isLowSurrogateHalf(string.charAt(offset));
|
||||
}
|
||||
/**
|
||||
* Checks whether given offset in a string is between base character and combining mark or between two combining marks.
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export function isInsideCombinedSymbol(string, offset) {
|
||||
return isCombiningMark(string.charAt(offset));
|
||||
}
|
||||
const EMOJI_PATTERN = buildEmojiRegexp();
|
||||
/**
|
||||
* Checks whether given offset in a string is inside multi-character emoji sequence.
|
||||
*
|
||||
* @param string String to check.
|
||||
* @param offset Offset to check.
|
||||
*/
|
||||
export function isInsideEmojiSequence(string, offset) {
|
||||
const matches = String(string).matchAll(EMOJI_PATTERN);
|
||||
return Array.from(matches).some(match => match.index < offset && offset < match.index + match[0].length);
|
||||
}
|
||||
function buildEmojiRegexp() {
|
||||
const parts = [
|
||||
// Emoji Tag Sequence (ETS)
|
||||
/\p{Emoji}[\u{E0020}-\u{E007E}]+\u{E007F}/u,
|
||||
// Emoji Keycap Sequence
|
||||
/\p{Emoji}\u{FE0F}?\u{20E3}/u,
|
||||
// Emoji Presentation Sequence
|
||||
/\p{Emoji}\u{FE0F}/u,
|
||||
// Single-Character Emoji / Emoji Modifier Sequence
|
||||
/(?=\p{General_Category=Other_Symbol})\p{Emoji}\p{Emoji_Modifier}*/u
|
||||
];
|
||||
const flagSequence = /\p{Regional_Indicator}{2}/u.source;
|
||||
const emoji = '(?:' + parts.map(part => part.source).join('|') + ')';
|
||||
const sequence = `${flagSequence}|${emoji}(?:\u{200D}${emoji})*`;
|
||||
return new RegExp(sequence, 'ug');
|
||||
}
|
||||
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/verifylicense.d.ts
generated
vendored
Normal file
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/verifylicense.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* Possible states of the key after verification.
|
||||
*/
|
||||
export type VerifiedKeyStatus = 'VALID' | 'INVALID';
|
||||
/**
|
||||
* Checks whether the given string contains information that allows you to verify the license status.
|
||||
*
|
||||
* @param token The string to check.
|
||||
* @returns String that represents the state of given `token` parameter.
|
||||
*/
|
||||
export default function verifyLicense(token: string | undefined): VerifiedKeyStatus;
|
||||
87
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/verifylicense.js
generated
vendored
Normal file
87
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/verifylicense.js
generated
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* @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/verifylicense
|
||||
*/
|
||||
import { releaseDate } from './version.js';
|
||||
/**
|
||||
* Checks whether the given string contains information that allows you to verify the license status.
|
||||
*
|
||||
* @param token The string to check.
|
||||
* @returns String that represents the state of given `token` parameter.
|
||||
*/
|
||||
export default function verifyLicense(token) {
|
||||
// This function implements naive and partial license key check mechanism,
|
||||
// used only to decide whether to show or hide the "Powered by CKEditor" logo.
|
||||
//
|
||||
// You can read the reasoning behind showing the logo to unlicensed (GPL) users
|
||||
// in this thread: https://github.com/ckeditor/ckeditor5/issues/14082.
|
||||
//
|
||||
// We firmly believe in the values behind creating open-source software, even when that
|
||||
// means keeping the license verification logic open for everyone to see.
|
||||
//
|
||||
// Please keep this code intact. Thank you for your understanding.
|
||||
function oldTokenCheck(token) {
|
||||
if (token.length >= 40 && token.length <= 255) {
|
||||
return 'VALID';
|
||||
}
|
||||
else {
|
||||
return 'INVALID';
|
||||
}
|
||||
}
|
||||
// TODO: issue ci#3175
|
||||
if (!token) {
|
||||
return 'INVALID';
|
||||
}
|
||||
let decryptedData = '';
|
||||
try {
|
||||
decryptedData = atob(token);
|
||||
}
|
||||
catch (e) {
|
||||
return 'INVALID';
|
||||
}
|
||||
const splittedDecryptedData = decryptedData.split('-');
|
||||
const firstElement = splittedDecryptedData[0];
|
||||
const secondElement = splittedDecryptedData[1];
|
||||
if (!secondElement) {
|
||||
return oldTokenCheck(token);
|
||||
}
|
||||
try {
|
||||
atob(secondElement);
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
atob(firstElement);
|
||||
if (!atob(firstElement).length) {
|
||||
return oldTokenCheck(token);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return oldTokenCheck(token);
|
||||
}
|
||||
}
|
||||
if (firstElement.length < 40 || firstElement.length > 255) {
|
||||
return 'INVALID';
|
||||
}
|
||||
let decryptedSecondElement = '';
|
||||
try {
|
||||
atob(firstElement);
|
||||
decryptedSecondElement = atob(secondElement);
|
||||
}
|
||||
catch (e) {
|
||||
return 'INVALID';
|
||||
}
|
||||
if (decryptedSecondElement.length !== 8) {
|
||||
return 'INVALID';
|
||||
}
|
||||
const year = Number(decryptedSecondElement.substring(0, 4));
|
||||
const monthIndex = Number(decryptedSecondElement.substring(4, 6)) - 1;
|
||||
const day = Number(decryptedSecondElement.substring(6, 8));
|
||||
const date = new Date(year, monthIndex, day);
|
||||
if (date < releaseDate || isNaN(Number(date))) {
|
||||
return 'INVALID';
|
||||
}
|
||||
return 'VALID';
|
||||
}
|
||||
10
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/version.d.ts
generated
vendored
Normal file
10
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/version.d.ts
generated
vendored
Normal file
|
|
@ -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
|
||||
*/
|
||||
declare const version = "41.3.1";
|
||||
export default version;
|
||||
export declare const releaseDate: Date;
|
||||
declare global {
|
||||
var CKEDITOR_VERSION: string;
|
||||
}
|
||||
153
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/version.js
generated
vendored
Normal file
153
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/version.js
generated
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* @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/version
|
||||
*/
|
||||
import CKEditorError from './ckeditorerror.js';
|
||||
const version = '41.3.1';
|
||||
export default version;
|
||||
// The second argument is not a month. It is `monthIndex` and starts from `0`.
|
||||
export const releaseDate = new Date(2024, 3, 16);
|
||||
/* istanbul ignore next -- @preserve */
|
||||
if (globalThis.CKEDITOR_VERSION) {
|
||||
/**
|
||||
* This error is thrown when due to a mistake in how CKEditor 5 was installed or initialized, some
|
||||
* of its modules were duplicated (evaluated and executed twice). Module duplication leads to inevitable runtime
|
||||
* errors.
|
||||
*
|
||||
* There are many situations in which some modules can be loaded twice. In the worst case scenario,
|
||||
* you may need to check your project for each of these issues and fix them all.
|
||||
*
|
||||
* # Trying to add a plugin to an existing build
|
||||
*
|
||||
* If you import an existing CKEditor 5 build and a plugin like this:
|
||||
*
|
||||
* ```ts
|
||||
* import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
* import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
|
||||
* ```
|
||||
*
|
||||
* Then your project loads some CKEditor 5 packages twice. How does it happen?
|
||||
*
|
||||
* The build package contains a file which is already compiled with webpack. This means
|
||||
* that it contains all the necessary code from e.g. `@ckeditor/ckeditor5-engine` and `@ckeditor/ckeditor5-utils`.
|
||||
*
|
||||
* However, the `Highlight` plugin imports some of the modules from these packages, too. If you ask webpack to
|
||||
* build such a project, you will end up with the modules being included (and run) twice – first, because they are
|
||||
* included inside the build package, and second, because they are required by the `Highlight` plugin.
|
||||
*
|
||||
* Therefore, **you must never add plugins to an existing build** unless your plugin has no dependencies.
|
||||
*
|
||||
* Adding plugins to a build is done by taking the source version of this build (so, before it was built with webpack)
|
||||
* and adding plugins there. In this situation, webpack will know that it only needs to load each plugin once.
|
||||
*
|
||||
* Read more in the {@glink installation/plugins/installing-plugins Installing plugins} guide.
|
||||
*
|
||||
* # Confused an editor build with an editor implementation
|
||||
*
|
||||
* This scenario is very similar to the previous one, but has a different origin.
|
||||
*
|
||||
* Let's assume that you wanted to use CKEditor 5 from source, as explained in the
|
||||
* {@glink installation/advanced/alternative-setups/integrating-from-source-webpack "Building from source"} section
|
||||
* or in the {@glink framework/quick-start "Quick start"} guide of CKEditor 5 Framework.
|
||||
*
|
||||
* The correct way to do so is to import an editor and plugins and run them together like this:
|
||||
*
|
||||
* ```ts
|
||||
* import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
|
||||
* import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
|
||||
* import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
|
||||
* import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
|
||||
* import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
|
||||
*
|
||||
* ClassicEditor
|
||||
* .create( document.querySelector( '#editor' ), {
|
||||
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
||||
* toolbar: [ 'bold', 'italic' ]
|
||||
* } )
|
||||
* .then( editor => {
|
||||
* console.log( 'Editor was initialized', editor );
|
||||
* } )
|
||||
* .catch( error => {
|
||||
* console.error( error.stack );
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* However, you might have mistakenly imported a build instead of the source `ClassicEditor`. In this case
|
||||
* your imports will look like this:
|
||||
*
|
||||
* ```ts
|
||||
* import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
* import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
|
||||
* import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
|
||||
* import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
|
||||
* import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
|
||||
* ```
|
||||
*
|
||||
* This creates the same situation as in the previous section because you use a build together with source plugins.
|
||||
*
|
||||
* Remember: `@ckeditor/ckeditor5-build-*` packages contain editor builds and `@ckeditor/ckeditor5-editor-*` contain source editors.
|
||||
*
|
||||
* # Loading two or more builds on one page
|
||||
*
|
||||
* If you use CKEditor 5 builds, you might have loaded two (or more) `ckeditor.js` files on one web page.
|
||||
* Check your web page for duplicated `<script>` elements or make sure your page builder/bundler includes CKEditor only once.
|
||||
*
|
||||
* If you want to use two different types of editors at once, see the
|
||||
* {@glink installation/advanced/using-two-editors "Using two different editors"}
|
||||
* section.
|
||||
*
|
||||
* # Using outdated packages
|
||||
*
|
||||
* Building CKEditor 5 from source requires using multiple npm packages. These packages have their dependencies
|
||||
* to other packages. If you use the latest version of, for example, `@ckeditor/ckeditor5-editor-classic` with
|
||||
* an outdated version of `@ckeditor/ckeditor5-image`, npm or yarn will need to install two different versions of
|
||||
* `@ckeditor/ckeditor5-core` because `@ckeditor/ckeditor5-editor-classic` and `@ckeditor/ckeditor5-image` may require
|
||||
* different versions of the core package.
|
||||
*
|
||||
* The solution to this issue is to update all packages to their latest version. We recommend
|
||||
* using tools like [`npm-check-updates`](https://www.npmjs.com/package/npm-check-updates) which simplify this process.
|
||||
*
|
||||
* # Conflicting version of dependencies
|
||||
*
|
||||
* This is a special case of the previous scenario. If you use CKEditor 5 with some third-party plugins,
|
||||
* it may happen that even if you use the latest versions of the official packages and the latest version of
|
||||
* these third-party packages, there will be a conflict between some of their dependencies.
|
||||
*
|
||||
* Such a problem can be resolved by either downgrading CKEditor 5 packages (which we do not recommend) or
|
||||
* asking the author of the third-party package to upgrade its depdendencies (or forking their project and doing this yourself).
|
||||
*
|
||||
* **Note:** All official CKEditor 5 packages (excluding integrations and `ckeditor5-dev-*` packages) are released in the
|
||||
* same major version. This means that in the `x.y.z` version, the `x` is the same for all packages. This is the simplest way to check
|
||||
* whether you use packages coming from the same CKEditor 5 version. You can read more about versioning in the
|
||||
* {@glink updating/versioning-policy Versioning policy} guide.
|
||||
*
|
||||
* # Packages were duplicated in `node_modules`
|
||||
*
|
||||
* In some situations, especially when calling `npm install` multiple times, it may happen
|
||||
* that npm will not correctly "deduplicate" packages.
|
||||
*
|
||||
* Normally, npm deduplicates all packages so, for example, `@ckeditor/ckeditor5-core` is installed only once in `node_modules/`.
|
||||
* However, it is known to fail to do so from time to time.
|
||||
*
|
||||
* We recommend checking if any of the steps listed below help:
|
||||
*
|
||||
* * `rm -rf node_modules && npm install` to make sure you have a clean `node_modules/` directory. This step
|
||||
* is known to help in most cases.
|
||||
* * If you use `yarn.lock` or `package-lock.json`, remove it before `npm install`.
|
||||
* * Check whether all CKEditor 5 packages are up to date and reinstall them
|
||||
* if you changed anything (`rm -rf node_modules && npm install`).
|
||||
*
|
||||
* If all packages are correct and compatible with each other, the steps above are known to help. If not, you may
|
||||
* try to check with `npm ls` how many times packages like `@ckeditor/ckeditor5-core`, `@ckeditor/ckeditor5-engine` and
|
||||
*`@ckeditor/ckeditor5-utils` are installed. If more than once, verify which package causes that.
|
||||
*
|
||||
* @error ckeditor-duplicated-modules
|
||||
*/
|
||||
throw new CKEditorError('ckeditor-duplicated-modules', null);
|
||||
}
|
||||
else {
|
||||
globalThis.CKEDITOR_VERSION = version;
|
||||
}
|
||||
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/wait.d.ts
generated
vendored
Normal file
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/wait.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @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/wait
|
||||
*/
|
||||
/**
|
||||
* Returns a promise that is resolved after the specified time.
|
||||
*
|
||||
* @param timeout The time in milliseconds to wait.
|
||||
* @param options.signal A signal to abort the waiting.
|
||||
*/
|
||||
export default function wait(timeout: number, options?: {
|
||||
signal?: AbortSignal;
|
||||
}): Promise<void>;
|
||||
29
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/wait.js
generated
vendored
Normal file
29
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-utils/src/wait.js
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @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/wait
|
||||
*/
|
||||
/**
|
||||
* Returns a promise that is resolved after the specified time.
|
||||
*
|
||||
* @param timeout The time in milliseconds to wait.
|
||||
* @param options.signal A signal to abort the waiting.
|
||||
*/
|
||||
export default function wait(timeout, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const signal = options.signal || (new AbortController()).signal;
|
||||
signal.throwIfAborted();
|
||||
const timer = setTimeout(timeoutHandler, timeout);
|
||||
signal.addEventListener('abort', abortHandler, { once: true });
|
||||
function timeoutHandler() {
|
||||
signal.removeEventListener('abort', abortHandler);
|
||||
resolve();
|
||||
}
|
||||
function abortHandler() {
|
||||
clearTimeout(timer);
|
||||
reject(signal.reason);
|
||||
}
|
||||
});
|
||||
}
|
||||
75
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/CHANGELOG.md
generated
vendored
Normal file
75
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
All changes in the package are documented in the main repository. See: https://github.com/ckeditor/ckeditor5/blob/master/CHANGELOG.md.
|
||||
|
||||
Changes for the past releases are available below.
|
||||
|
||||
## [19.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v18.0.0...v19.0.0) (April 29, 2020)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [18.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v17.0.0...v18.0.0) (March 19, 2020)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [17.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v16.0.0...v17.0.0) (February 19, 2020)
|
||||
|
||||
### MAJOR BREAKING CHANGES
|
||||
|
||||
* The `Watchdog` class was renamed to `EditorWatchdog` and is available in `src/editorwatchdog.js`.
|
||||
* The `EditorWatchdog.for()` method was removed in favor of the constructor.
|
||||
* The `EditorWatchdog#constructor()` API changed. Now the `EditorWatchdog` constructor accepts the editor class as the first argument and the watchdog configuration as the second argument. The `EditorWatchdog` editor creator now defaults to `( sourceElementOrData, config ) => Editor.create( sourceElementOrData, config )`.
|
||||
|
||||
### Features
|
||||
|
||||
* Introduced `ContextWatchdog` which is a watchdog for `Context`. Closes [ckeditor/ckeditor5#6079](https://github.com/ckeditor/ckeditor5/issues/6079). Closes [ckeditor/ckeditor5#6042](https://github.com/ckeditor/ckeditor5/issues/6042). Closes [ckeditor/ckeditor5#4696](https://github.com/ckeditor/ckeditor5/issues/4696). ([76c4938](https://github.com/ckeditor/ckeditor5-watchdog/commit/76c4938))
|
||||
|
||||
|
||||
## [16.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v15.0.0...v16.0.0) (December 4, 2019)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Only instances of the `Error` class will be handled by `Watchdog`. Closes [ckeditor/ckeditor5#5678](https://github.com/ckeditor/ckeditor5/issues/5678). ([3f24a2e](https://github.com/ckeditor/ckeditor5-watchdog/commit/3f24a2e))
|
||||
|
||||
|
||||
## [15.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v11.0.0...v15.0.0) (October 23, 2019)
|
||||
|
||||
### Other changes
|
||||
|
||||
* Made the Watchdog#setDestructor() method optional and default to editor => editor.destroy(). Closes [#21](https://github.com/ckeditor/ckeditor5-watchdog/issues/21). ([5a9dc0c](https://github.com/ckeditor/ckeditor5-watchdog/commit/5a9dc0c))
|
||||
|
||||
|
||||
## [11.0.0](https://github.com/ckeditor/ckeditor5-watchdog/compare/v10.0.1...v11.0.0) (August 26, 2019)
|
||||
|
||||
### Features
|
||||
|
||||
* Added support for multi-root editors. Closes [#22](https://github.com/ckeditor/ckeditor5-watchdog/issues/22). ([692955e](https://github.com/ckeditor/ckeditor5-watchdog/commit/692955e))
|
||||
* Added unhandled promise rejection error handling. Fixed objects in the `crashed` array. Closes [#3](https://github.com/ckeditor/ckeditor5-watchdog/issues/3). ([1a47364](https://github.com/ckeditor/ckeditor5-watchdog/commit/1a47364))
|
||||
* Introduced the observable `Watchdog#state` property. Introduced the `minimumNonErrorTimePeriod` configuration which defaults to 5 seconds and will be used to prevent infinite restart loops while allowing a larger number of random crashes as long as they do not happen too often. Renamed `waitingTime` configuration option to `saveInterval`. Closes [#7](https://github.com/ckeditor/ckeditor5-watchdog/issues/7). Closes [#15](https://github.com/ckeditor/ckeditor5-watchdog/issues/15). ([5bdbfe5](https://github.com/ckeditor/ckeditor5-watchdog/commit/5bdbfe5))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* The editor data will be saved correctly after the `destroy()` method is called. Added the protected `Watchdog#_now()` method that allows for time-based testing of the error handling mechanism. Closes [#17](https://github.com/ckeditor/ckeditor5-watchdog/issues/17). Closes [#19](https://github.com/ckeditor/ckeditor5-watchdog/issues/19). ([a54db15](https://github.com/ckeditor/ckeditor5-watchdog/commit/a54db15))
|
||||
|
||||
### Other changes
|
||||
|
||||
* The issue tracker for this package was moved to https://github.com/ckeditor/ckeditor5/issues. See [ckeditor/ckeditor5#1988](https://github.com/ckeditor/ckeditor5/issues/1988). ([741594a](https://github.com/ckeditor/ckeditor5-watchdog/commit/741594a))
|
||||
* Made the `Watchdog#restart()` method private. Changed the signatures of `Watchdog#create()` and `Watchdog#destroy()`, so now these methods will return empty promises. Closes [#13](https://github.com/ckeditor/ckeditor5-watchdog/issues/13). ([69aef8b](https://github.com/ckeditor/ckeditor5-watchdog/commit/69aef8b))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Renamed `waitingTime` configuration option to `saveInterval`.
|
||||
* `Watchdog#restart()` is no longer public.
|
||||
|
||||
|
||||
## [10.0.1](https://github.com/ckeditor/ckeditor5-watchdog/compare/v10.0.0...v10.0.1) (July 10, 2019)
|
||||
|
||||
Internal changes only (updated dependencies, documentation, etc.).
|
||||
|
||||
|
||||
## [10.0.0](https://github.com/ckeditor/ckeditor5-watchdog/tree/v10.0.0) (July 4, 2019)
|
||||
|
||||
The initial watchdog feature implementation.
|
||||
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/LICENSE.md
generated
vendored
Normal file
21
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/LICENSE.md
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
Software License Agreement
|
||||
==========================
|
||||
|
||||
**CKEditor 5 watchdog feature** – https://github.com/ckeditor/ckeditor5-watchdog <br>
|
||||
Copyright (c) 2003–2024, [CKSource Holding sp. z o.o.](https://cksource.com) All rights reserved.
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
||||
|
||||
Sources of Intellectual Property Included in CKEditor
|
||||
-----------------------------------------------------
|
||||
|
||||
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
||||
|
||||
The following libraries are included in CKEditor under the [MIT license](https://opensource.org/licenses/MIT):
|
||||
|
||||
* Lodash - Copyright (c) JS Foundation and other contributors https://js.foundation/. Based on Underscore.js, copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors http://underscorejs.org/.
|
||||
|
||||
Trademarks
|
||||
----------
|
||||
|
||||
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](https://cksource.com) All other brand and product names are trademarks, registered trademarks, or service marks of their respective holders.
|
||||
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/README.md
generated
vendored
Normal file
16
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
CKEditor 5 watchdog feature
|
||||
========================================
|
||||
|
||||
[](https://www.npmjs.com/package/@ckeditor/ckeditor5-watchdog)
|
||||
[](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
|
||||
[](https://app.travis-ci.com/github/ckeditor/ckeditor5)
|
||||
|
||||
This package implements the watchdog feature for CKEditor 5. It keeps a CKEditor 5 rich-text editor instance running.
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [`@ckeditor/ckeditor5-watchdog` package](https://ckeditor.com/docs/ckeditor5/latest/api/watchdog.html) page as well as the [Watchdog feature](https://ckeditor.com/docs/ckeditor5/latest/features/watchdog.html) guide in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
|
||||
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/ckeditor5-metadata.json
generated
vendored
Normal file
11
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/ckeditor5-metadata.json
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"plugins": [
|
||||
{
|
||||
"name": "Watchdog",
|
||||
"className": "EditorWatchdog",
|
||||
"description": "A utility that detects if an editor crashed, destroys it, and automatically creates a new instance of that editor with the content of the previous editor.",
|
||||
"docs": "features/watchdog.html",
|
||||
"path": "src/editorwatchdog.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
35
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/package.json
generated
vendored
Normal file
35
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "@ckeditor/ckeditor5-watchdog",
|
||||
"version": "41.3.1",
|
||||
"description": "A watchdog feature for CKEditor 5 editors. It keeps a CKEditor 5 editor instance running.",
|
||||
"keywords": [
|
||||
"ckeditor",
|
||||
"ckeditor5",
|
||||
"ckeditor 5",
|
||||
"ckeditor5-lib",
|
||||
"ckeditor5-dll"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "src/index.js",
|
||||
"dependencies": {
|
||||
"lodash-es": "4.17.21"
|
||||
},
|
||||
"author": "CKSource (http://cksource.com/)",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"homepage": "https://ckeditor.com/ckeditor-5",
|
||||
"bugs": "https://github.com/ckeditor/ckeditor5/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ckeditor/ckeditor5.git",
|
||||
"directory": "packages/ckeditor5-watchdog"
|
||||
},
|
||||
"files": [
|
||||
"lang",
|
||||
"src/**/*.js",
|
||||
"src/**/*.d.ts",
|
||||
"theme",
|
||||
"ckeditor5-metadata.json",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"types": "src/index.d.ts"
|
||||
}
|
||||
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/augmentation.d.ts
generated
vendored
Normal file
15
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/augmentation.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @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 type { EditorData } from './editorwatchdog.js';
|
||||
declare module '@ckeditor/ckeditor5-core' {
|
||||
interface EditorConfig {
|
||||
/**
|
||||
* The temporary property that is used for passing data to the plugin which restores the editor state.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_watchdogInitialData?: EditorData;
|
||||
}
|
||||
}
|
||||
5
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/augmentation.js
generated
vendored
Normal file
5
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/augmentation.js
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* @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
|
||||
*/
|
||||
export {};
|
||||
333
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/contextwatchdog.d.ts
generated
vendored
Normal file
333
vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-watchdog/src/contextwatchdog.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
/**
|
||||
* @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 watchdog/contextwatchdog
|
||||
*/
|
||||
import type { Context, Editor, EditorConfig, ContextConfig } from 'ckeditor5/src/core.js';
|
||||
import type { ArrayOrItem, CKEditorError } from 'ckeditor5/src/utils.js';
|
||||
import Watchdog, { type WatchdogConfig, type WatchdogState } from './watchdog.js';
|
||||
import EditorWatchdog, { type EditorCreatorFunction } from './editorwatchdog.js';
|
||||
/**
|
||||
* A watchdog for the {@link module:core/context~Context} class.
|
||||
*
|
||||
* See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and
|
||||
* how to use it.
|
||||
*/
|
||||
export default class ContextWatchdog<TContext extends Context = Context> extends Watchdog {
|
||||
/**
|
||||
* A map of internal watchdogs for added items.
|
||||
*/
|
||||
protected _watchdogs: Map<string, EditorWatchdog<Editor>>;
|
||||
/**
|
||||
* The watchdog configuration.
|
||||
*/
|
||||
private readonly _watchdogConfig;
|
||||
/**
|
||||
* The current context instance.
|
||||
*/
|
||||
private _context;
|
||||
/**
|
||||
* Context properties (nodes/references) that are gathered during the initial context creation
|
||||
* and are used to distinguish the origin of an error.
|
||||
*/
|
||||
private _contextProps;
|
||||
/**
|
||||
* An action queue, which is used to handle async functions queuing.
|
||||
*/
|
||||
private _actionQueues;
|
||||
/**
|
||||
* The configuration for the {@link module:core/context~Context}.
|
||||
*/
|
||||
private _contextConfig?;
|
||||
/**
|
||||
* The creation method.
|
||||
*
|
||||
* @see #setCreator
|
||||
*/
|
||||
protected _creator: (config: ContextConfig) => Promise<TContext>;
|
||||
/**
|
||||
* The destruction method.
|
||||
*
|
||||
* @see #setDestructor
|
||||
*/
|
||||
protected _destructor: (context: Context) => Promise<unknown>;
|
||||
/**
|
||||
* The watched item.
|
||||
*/
|
||||
_item: unknown;
|
||||
/**
|
||||
* The context watchdog class constructor.
|
||||
*
|
||||
* ```ts
|
||||
* const watchdog = new ContextWatchdog( Context );
|
||||
*
|
||||
* await watchdog.create( contextConfiguration );
|
||||
*
|
||||
* await watchdog.add( item );
|
||||
* ```
|
||||
*
|
||||
* See the {@glink features/watchdog Watchdog feature guide} to learn more how to use this feature.
|
||||
*
|
||||
* @param Context The {@link module:core/context~Context} class.
|
||||
* @param watchdogConfig The watchdog configuration.
|
||||
*/
|
||||
constructor(Context: {
|
||||
create(...args: any): Promise<TContext>;
|
||||
}, watchdogConfig?: WatchdogConfig);
|
||||
/**
|
||||
* Sets the function that is responsible for the context creation.
|
||||
* It expects a function that should return a promise (or `undefined`).
|
||||
*
|
||||
* ```ts
|
||||
* watchdog.setCreator( config => Context.create( config ) );
|
||||
* ```
|
||||
*/
|
||||
setCreator(creator: (config: ContextConfig) => Promise<TContext>): void;
|
||||
/**
|
||||
* Sets the function that is responsible for the context destruction.
|
||||
* Overrides the default destruction function, which destroys only the context instance.
|
||||
* It expects a function that should return a promise (or `undefined`).
|
||||
*
|
||||
* ```ts
|
||||
* watchdog.setDestructor( context => {
|
||||
* // Do something before the context is destroyed.
|
||||
*
|
||||
* return context
|
||||
* .destroy()
|
||||
* .then( () => {
|
||||
* // Do something after the context is destroyed.
|
||||
* } );
|
||||
* } );
|
||||
* ```
|
||||
*/
|
||||
setDestructor(destructor: (context: Context) => Promise<unknown>): void;
|
||||
/**
|
||||
* The context instance. Keep in mind that this property might be changed when the context watchdog restarts,
|
||||
* so do not keep this instance internally. Always operate on the `ContextWatchdog#context` property.
|
||||
*/
|
||||
get context(): Context | null;
|
||||
/**
|
||||
* Initializes the context watchdog. Once it is created, the watchdog takes care about
|
||||
* recreating the context and the provided items, and starts the error handling mechanism.
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.create( {
|
||||
* plugins: []
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* @param contextConfig The context configuration. See {@link module:core/context~Context}.
|
||||
*/
|
||||
create(contextConfig?: ContextConfig): Promise<unknown>;
|
||||
/**
|
||||
* Returns an item instance with the given `itemId`.
|
||||
*
|
||||
* ```ts
|
||||
* const editor1 = watchdog.getItem( 'editor1' );
|
||||
* ```
|
||||
*
|
||||
* @param itemId The item ID.
|
||||
* @returns The item instance or `undefined` if an item with a given ID has not been found.
|
||||
*/
|
||||
getItem(itemId: string): unknown;
|
||||
/**
|
||||
* Gets the state of the given item. See {@link #state} for a list of available states.
|
||||
*
|
||||
* ```ts
|
||||
* const editor1State = watchdog.getItemState( 'editor1' );
|
||||
* ```
|
||||
*
|
||||
* @param itemId Item ID.
|
||||
* @returns The state of the item.
|
||||
*/
|
||||
getItemState(itemId: string): WatchdogState;
|
||||
/**
|
||||
* Adds items to the watchdog. Once created, instances of these items will be available using the {@link #getItem} method.
|
||||
*
|
||||
* Items can be passed together as an array of objects:
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.add( [ {
|
||||
* id: 'editor1',
|
||||
* type: 'editor',
|
||||
* sourceElementOrData: document.querySelector( '#editor' ),
|
||||
* config: {
|
||||
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
||||
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
||||
* },
|
||||
* creator: ( element, config ) => ClassicEditor.create( element, config )
|
||||
* } ] );
|
||||
* ```
|
||||
*
|
||||
* Or one by one as objects:
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.add( {
|
||||
* id: 'editor1',
|
||||
* type: 'editor',
|
||||
* sourceElementOrData: document.querySelector( '#editor' ),
|
||||
* config: {
|
||||
* plugins: [ Essentials, Paragraph, Bold, Italic ],
|
||||
* toolbar: [ 'bold', 'italic', 'alignment' ]
|
||||
* },
|
||||
* creator: ( element, config ) => ClassicEditor.create( element, config )
|
||||
* ] );
|
||||
* ```
|
||||
*
|
||||
* Then an instance can be retrieved using the {@link #getItem} method:
|
||||
*
|
||||
* ```ts
|
||||
* const editor1 = watchdog.getItem( 'editor1' );
|
||||
* ```
|
||||
*
|
||||
* Note that this method can be called multiple times, but for performance reasons it is better
|
||||
* to pass all items together.
|
||||
*
|
||||
* @param itemConfigurationOrItemConfigurations An item configuration object or an array of item configurations.
|
||||
*/
|
||||
add(itemConfigurationOrItemConfigurations: ArrayOrItem<WatchdogItemConfiguration>): Promise<unknown>;
|
||||
/**
|
||||
* Removes and destroys item(s) with given ID(s).
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.remove( 'editor1' );
|
||||
* ```
|
||||
*
|
||||
* Or
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.remove( [ 'editor1', 'editor2' ] );
|
||||
* ```
|
||||
*
|
||||
* @param itemIdOrItemIds Item ID or an array of item IDs.
|
||||
*/
|
||||
remove(itemIdOrItemIds: ArrayOrItem<string>): Promise<unknown>;
|
||||
/**
|
||||
* Destroys the context watchdog and all added items.
|
||||
* Once the context watchdog is destroyed, new items cannot be added.
|
||||
*
|
||||
* ```ts
|
||||
* await watchdog.destroy();
|
||||
* ```
|
||||
*/
|
||||
destroy(): Promise<unknown>;
|
||||
/**
|
||||
* Restarts the context watchdog.
|
||||
*/
|
||||
protected _restart(): Promise<unknown>;
|
||||
/**
|
||||
* Initializes the context watchdog.
|
||||
*/
|
||||
private _create;
|
||||
/**
|
||||
* Destroys the context instance and all added items.
|
||||
*/
|
||||
private _destroy;
|
||||
/**
|
||||
* Returns the watchdog for a given item ID.
|
||||
*
|
||||
* @param itemId Item ID.
|
||||
*/
|
||||
protected _getWatchdog(itemId: string): Watchdog;
|
||||
/**
|
||||
* Checks whether an error comes from the context instance and not from the item instances.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_isErrorComingFromThisItem(error: CKEditorError): boolean;
|
||||
}
|
||||
/**
|
||||
* Fired after the watchdog restarts the context and the added items because of a crash.
|
||||
*
|
||||
* ```ts
|
||||
* watchdog.on( 'restart', () => {
|
||||
* console.log( 'The context has been restarted.' );
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* @eventName ~ContextWatchdog#restart
|
||||
*/
|
||||
export type ContextWatchdogRestartEvent = {
|
||||
name: 'restart';
|
||||
args: [];
|
||||
return: undefined;
|
||||
};
|
||||
/**
|
||||
* Fired when a new error occurred in one of the added items.
|
||||
*
|
||||
* ```ts
|
||||
* watchdog.on( 'itemError', ( evt, { error, itemId } ) => {
|
||||
* console.log( `An error occurred in an item with the '${ itemId }' ID.` );
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* @eventName ~ContextWatchdog#itemError
|
||||
*/
|
||||
export type ContextWatchdogItemErrorEvent = {
|
||||
name: 'itemError';
|
||||
args: [ContextWatchdogItemErrorEventData];
|
||||
return: undefined;
|
||||
};
|
||||
/**
|
||||
* The `itemError` event data.
|
||||
*/
|
||||
export type ContextWatchdogItemErrorEventData = {
|
||||
itemId: string;
|
||||
error: Error;
|
||||
};
|
||||
/**
|
||||
* Fired after an item has been restarted.
|
||||
*
|
||||
* ```ts
|
||||
* watchdog.on( 'itemRestart', ( evt, { itemId } ) => {
|
||||
* console.log( 'An item with with the '${ itemId }' ID has been restarted.' );
|
||||
* } );
|
||||
* ```
|
||||
*
|
||||
* @eventName ~ContextWatchdog#itemRestart
|
||||
*/
|
||||
export type ContextWatchdogItemRestartEvent = {
|
||||
name: 'itemRestart';
|
||||
args: [ContextWatchdogItemRestartEventData];
|
||||
return: undefined;
|
||||
};
|
||||
/**
|
||||
* The `itemRestart` event data.
|
||||
*/
|
||||
export type ContextWatchdogItemRestartEventData = {
|
||||
itemId: string;
|
||||
};
|
||||
/**
|
||||
* The watchdog item configuration interface.
|
||||
*/
|
||||
export interface WatchdogItemConfiguration {
|
||||
/**
|
||||
* id A unique item identificator.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The type of the item to create. At the moment, only `'editor'` is supported.
|
||||
*/
|
||||
type: 'editor';
|
||||
/**
|
||||
* A function that initializes the item (the editor). The function takes editor initialization arguments
|
||||
* and should return a promise. For example: `( el, config ) => ClassicEditor.create( el, config )`.
|
||||
*/
|
||||
creator: EditorCreatorFunction;
|
||||
/**
|
||||
* A function that destroys the item instance (the editor). The function
|
||||
* takes an item and should return a promise. For example: `editor => editor.destroy()`
|
||||
*/
|
||||
destructor?: (editor: Editor) => Promise<unknown>;
|
||||
/**
|
||||
* The source element or data that will be passed
|
||||
* as the first argument to the `Editor.create()` method.
|
||||
*/
|
||||
sourceElementOrData: string | HTMLElement;
|
||||
/**
|
||||
* An editor configuration.
|
||||
*/
|
||||
config: EditorConfig;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue