` which\n\t * wraps the editable element and the toolbar. In {@link module:editor-inline/inlineeditor~InlineEditor}\n\t * it is the editable element itself (as there is no other wrapper). However, in\n\t * {@link module:editor-decoupled/decouplededitor~DecoupledEditor} it is set to `null` because this editor does not\n\t * come with a single \"main\" HTML element (its editable element and toolbar are separate).\n\t *\n\t * This property can be understood as a shorthand for retrieving the element that a specific editor integration\n\t * considers to be its main DOM element.\n\t */\n\tpublic get element(): HTMLElement | null {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Fires the {@link module:ui/editorui/editorui~EditorUI#event:update `update`} event.\n\t *\n\t * This method should be called when the editor UI (e.g. positions of its balloons) needs to be updated due to\n\t * some environmental change which CKEditor 5 is not aware of (e.g. resize of a container in which it is used).\n\t */\n\tpublic update(): void {\n\t\tthis.fire
( 'update' );\n\t}\n\n\t/**\n\t * Destroys the UI.\n\t */\n\tpublic destroy(): void {\n\t\tthis.stopListening();\n\n\t\tthis.focusTracker.destroy();\n\t\tthis.tooltipManager.destroy( this.editor );\n\t\tthis.poweredBy.destroy();\n\n\t\t// Clean–up the references to the CKEditor instance stored in the native editable DOM elements.\n\t\tfor ( const domElement of this._editableElementsMap.values() ) {\n\t\t\t( domElement as any ).ckeditorInstance = null;\n\t\t\tthis.editor.keystrokes.stopListening( domElement );\n\t\t}\n\n\t\tthis._editableElementsMap = new Map();\n\t\tthis._focusableToolbarDefinitions = [];\n\t}\n\n\t/**\n\t * Stores the native DOM editable element used by the editor under a unique name.\n\t *\n\t * Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable\n\t * editable area, they can use the Alt + F10 keystroke to navigate over editor toolbars. See {@link #addToolbar}.\n\t *\n\t * @param rootName The unique name of the editable element.\n\t * @param domElement The native DOM editable element.\n\t */\n\tpublic setEditableElement( rootName: string, domElement: HTMLElement ): void {\n\t\tthis._editableElementsMap.set( rootName, domElement );\n\n\t\t// Put a reference to the CKEditor instance in the editable native DOM element.\n\t\t// It helps 3rd–party software (browser extensions, other libraries) access and recognize\n\t\t// CKEditor 5 instances (editing roots) and use their API (there is no global editor\n\t\t// instance registry).\n\t\tif ( !( domElement as any ).ckeditorInstance ) {\n\t\t\t( domElement as any ).ckeditorInstance = this.editor;\n\t\t}\n\n\t\t// Register the element, so it becomes available for Alt+F10 and Esc navigation.\n\t\tthis.focusTracker.add( domElement );\n\n\t\tconst setUpKeystrokeHandler = () => {\n\t\t\t// The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).\n\t\t\t// Do not duplicate listeners.\n\t\t\tif ( this.editor.editing.view.getDomRoot( rootName ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.editor.keystrokes.listenTo( domElement );\n\t\t};\n\n\t\t// For editable elements set by features after EditorUI is ready (e.g. source editing).\n\t\tif ( this.isReady ) {\n\t\t\tsetUpKeystrokeHandler();\n\t\t}\n\t\t// For editable elements set while the editor is being created (e.g. DOM roots).\n\t\telse {\n\t\t\tthis.once( 'ready', setUpKeystrokeHandler );\n\t\t}\n\t}\n\n\t/**\n\t * Removes the editable from the editor UI. Removes all handlers added by {@link #setEditableElement}.\n\t *\n\t * @param rootName The name of the editable element to remove.\n\t */\n\tpublic removeEditableElement( rootName: string ): void {\n\t\tconst domElement = this._editableElementsMap.get( rootName );\n\n\t\tif ( !domElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._editableElementsMap.delete( rootName );\n\n\t\tthis.editor.keystrokes.stopListening( domElement );\n\t\tthis.focusTracker.remove( domElement );\n\n\t\t( domElement as any ).ckeditorInstance = null;\n\t}\n\n\t/**\n\t * Returns the editable editor element with the given name or null if editable does not exist.\n\t *\n\t * @param rootName The editable name.\n\t */\n\tpublic getEditableElement( rootName: string = 'main' ): HTMLElement | undefined {\n\t\treturn this._editableElementsMap.get( rootName );\n\t}\n\n\t/**\n\t * Returns array of names of all editor editable elements.\n\t */\n\tpublic getEditableElementsNames(): IterableIterator {\n\t\treturn this._editableElementsMap.keys();\n\t}\n\n\t/**\n\t * Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.\n\t *\n\t * Focusable toolbars can be accessed (focused) by users by pressing the Alt + F10 keystroke.\n\t * Successive keystroke presses navigate over available toolbars.\n\t *\n\t * @param toolbarView A instance of the toolbar to be registered.\n\t */\n\tpublic addToolbar( toolbarView: ToolbarView, options: FocusableToolbarOptions = {} ): void {\n\t\tif ( toolbarView.isRendered ) {\n\t\t\tthis.focusTracker.add( toolbarView.element! );\n\t\t\tthis.editor.keystrokes.listenTo( toolbarView.element! );\n\t\t} else {\n\t\t\ttoolbarView.once( 'render', () => {\n\t\t\t\tthis.focusTracker.add( toolbarView.element! );\n\t\t\t\tthis.editor.keystrokes.listenTo( toolbarView.element! );\n\t\t\t} );\n\t\t}\n\n\t\tthis._focusableToolbarDefinitions.push( { toolbarView, options } );\n\t}\n\n\t/**\n\t * Stores all editable elements used by the editor instance.\n\t *\n\t * @deprecated\n\t */\n\tprotected get _editableElements(): unknown {\n\t\t/**\n\t\t * The {@link module:ui/editorui/editorui~EditorUI#_editableElements `EditorUI#_editableElements`} property has been\n\t\t * deprecated and will be removed in the near future. Please use\n\t\t * {@link module:ui/editorui/editorui~EditorUI#setEditableElement `setEditableElement()`} and\n\t\t * {@link module:ui/editorui/editorui~EditorUI#getEditableElement `getEditableElement()`} methods instead.\n\t\t *\n\t\t * @error editor-ui-deprecated-editable-elements\n\t\t * @param editorUI Editor UI instance the deprecated property belongs to.\n\t\t */\n\t\tconsole.warn(\n\t\t\t'editor-ui-deprecated-editable-elements: ' +\n\t\t\t'The EditorUI#_editableElements property has been deprecated and will be removed in the near future.',\n\t\t\t{ editorUI: this } );\n\n\t\treturn this._editableElementsMap;\n\t}\n\n\t/**\n\t * Returns viewport offsets object:\n\t *\n\t * ```js\n\t * {\n\t * \ttop: Number,\n\t * \tright: Number,\n\t * \tbottom: Number,\n\t * \tleft: Number\n\t * }\n\t * ```\n\t *\n\t * Only top property is currently supported.\n\t */\n\tprivate _readViewportOffsetFromConfig() {\n\t\tconst editor = this.editor;\n\t\tconst viewportOffsetConfig = editor.config.get( 'ui.viewportOffset' );\n\n\t\tif ( viewportOffsetConfig ) {\n\t\t\treturn viewportOffsetConfig;\n\t\t}\n\n\t\t// Not present in EditorConfig type, because it's legacy. Hence the `as` expression.\n\t\tconst legacyOffsetConfig = editor.config.get( 'toolbar.viewportTopOffset' ) as number | undefined;\n\n\t\t// Fall back to deprecated toolbar config.\n\t\tif ( legacyOffsetConfig ) {\n\t\t\t/**\n\t\t\t * The {@link module:core/editor/editorconfig~EditorConfig#toolbar `EditorConfig#toolbar.viewportTopOffset`}\n\t\t\t * property has been deprecated and will be removed in the near future. Please use\n\t\t\t * {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset`} instead.\n\t\t\t *\n\t\t\t * @error editor-ui-deprecated-viewport-offset-config\n\t\t\t */\n\t\t\tconsole.warn(\n\t\t\t\t'editor-ui-deprecated-viewport-offset-config: ' +\n\t\t\t\t'The `toolbar.vieportTopOffset` configuration option is deprecated. ' +\n\t\t\t\t'It will be removed from future CKEditor versions. Use `ui.viewportOffset.top` instead.'\n\t\t\t);\n\n\t\t\treturn { top: legacyOffsetConfig };\n\t\t}\n\n\t\t// More keys to come in the future.\n\t\treturn { top: 0 };\n\t}\n\n\t/**\n\t * Starts listening for Alt + F10 and Esc keystrokes in the context of focusable\n\t * {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}\n\t * to allow users navigate across the UI.\n\t */\n\tprivate _initFocusTracking(): void {\n\t\tconst editor = this.editor;\n\t\tconst editingView = editor.editing.view;\n\n\t\tlet lastFocusedForeignElement: HTMLElement | null;\n\t\tlet candidateDefinitions: Array;\n\n\t\t// Focus the next focusable toolbar on Alt + F10.\n\t\teditor.keystrokes.set( 'Alt+F10', ( data, cancel ) => {\n\t\t\tconst focusedElement = this.focusTracker.focusedElement as HTMLElement;\n\n\t\t\t// Focus moved out of a DOM element that\n\t\t\t// * is not a toolbar,\n\t\t\t// * does not belong to the editing view (e.g. source editing).\n\t\t\tif (\n\t\t\t\tArray.from( this._editableElementsMap.values() ).includes( focusedElement ) &&\n\t\t\t\t!Array.from( editingView.domRoots.values() ).includes( focusedElement )\n\t\t\t) {\n\t\t\t\tlastFocusedForeignElement = focusedElement;\n\t\t\t}\n\n\t\t\tconst currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();\n\n\t\t\t// * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.\n\t\t\t// This ensures, the navigation works always the same and no pair of toolbars takes over\n\t\t\t// (e.g. image and table toolbars when a selected image is inside a cell).\n\t\t\t// * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,\n\t\t\t// there were no candidates so they must be obtained (#12339).\n\t\t\tif ( !currentFocusedToolbarDefinition || !candidateDefinitions ) {\n\t\t\t\tcandidateDefinitions = this._getFocusableCandidateToolbarDefinitions();\n\t\t\t}\n\n\t\t\t// In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.\n\t\t\t// This prevents an infinite loop.\n\t\t\tfor ( let i = 0; i < candidateDefinitions.length; i++ ) {\n\t\t\t\tconst candidateDefinition = candidateDefinitions.shift()!;\n\n\t\t\t\t// Put the first definition to the back of the array. This allows circular navigation over all toolbars\n\t\t\t\t// on successive presses of Alt+F10.\n\t\t\t\tcandidateDefinitions.push( candidateDefinition );\n\n\t\t\t\t// Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the\n\t\t\t\t// first item as per ToolbarView#focus() if the user navigated inside the toolbar.\n\t\t\t\tif (\n\t\t\t\t\tcandidateDefinition !== currentFocusedToolbarDefinition &&\n\t\t\t\t\tthis._focusFocusableCandidateToolbar( candidateDefinition )\n\t\t\t\t) {\n\t\t\t\t\t// Clean up after a current visible toolbar when switching to the next one.\n\t\t\t\t\tif ( currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur ) {\n\t\t\t\t\t\tcurrentFocusedToolbarDefinition.options.afterBlur();\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcancel();\n\t\t} );\n\n\t\t// Blur the focused toolbar on Esc and bring the focus back to its origin.\n\t\teditor.keystrokes.set( 'Esc', ( data, cancel ) => {\n\t\t\tconst focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();\n\n\t\t\tif ( !focusedToolbarDef ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Bring focus back to where it came from before focusing the toolbar:\n\t\t\t// 1. If it came from outside the engine view (e.g. source editing), move it there.\n\t\t\tif ( lastFocusedForeignElement ) {\n\t\t\t\tlastFocusedForeignElement.focus();\n\t\t\t\tlastFocusedForeignElement = null;\n\t\t\t}\n\t\t\t// 2. There are two possibilities left:\n\t\t\t// 2.1. It could be that the focus went from an editable element in the view (root or nested).\n\t\t\t// 2.2. It could be the focus went straight to the toolbar before even focusing the editing area.\n\t\t\t// In either case, just focus the view editing. The focus will land where it belongs.\n\t\t\telse {\n\t\t\t\teditor.editing.view.focus();\n\t\t\t}\n\n\t\t\t// Clean up after the toolbar if there is anything to do there.\n\t\t\tif ( focusedToolbarDef.options.afterBlur ) {\n\t\t\t\tfocusedToolbarDef.options.afterBlur();\n\t\t\t}\n\n\t\t\tcancel();\n\t\t} );\n\t}\n\n\t/**\n\t * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.\n\t *\n\t * Focusable toolbars candidates are either:\n\t * * already visible,\n\t * * have `beforeFocus()` set in their {@link module:ui/editorui/editorui~FocusableToolbarDefinition definition} that suggests that\n\t * they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible\n\t * at this stage because it depends on its implementation, that in turn depends on the editing context (selection).\n\t *\n\t * **Note**: Contextual toolbars take precedence over regular toolbars.\n\t */\n\tprivate _getFocusableCandidateToolbarDefinitions(): Array {\n\t\tconst definitions: Array = [];\n\n\t\tfor ( const toolbarDef of this._focusableToolbarDefinitions ) {\n\t\t\tconst { toolbarView, options } = toolbarDef;\n\n\t\t\tif ( isVisible( toolbarView.element ) || options.beforeFocus ) {\n\t\t\t\tdefinitions.push( toolbarDef );\n\t\t\t}\n\t\t}\n\n\t\t// Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.\n\t\t// For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.\n\t\tdefinitions.sort( ( defA, defB ) => getToolbarDefinitionWeight( defA ) - getToolbarDefinitionWeight( defB ) );\n\n\t\treturn definitions;\n\t}\n\n\t/**\n\t * Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).\n\t *\n\t * `null` is returned when no toolbar is currently focused.\n\t */\n\tprivate _getCurrentFocusedToolbarDefinition(): FocusableToolbarDefinition | null {\n\t\tfor ( const definition of this._focusableToolbarDefinitions ) {\n\t\t\tif ( definition.toolbarView.element && definition.toolbarView.element.contains( this.focusTracker.focusedElement ) ) {\n\t\t\t\treturn definition;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Focuses a focusable toolbar candidate using its definition.\n\t *\n\t * @param candidateToolbarDefinition A definition of the toolbar to focus.\n\t * @returns `true` when the toolbar candidate was focused. `false` otherwise.\n\t */\n\tprivate _focusFocusableCandidateToolbar( candidateToolbarDefinition: FocusableToolbarDefinition ): boolean {\n\t\tconst { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;\n\n\t\tif ( beforeFocus ) {\n\t\t\tbeforeFocus();\n\t\t}\n\n\t\t// If it didn't show up after beforeFocus(), it's not focusable at all.\n\t\tif ( !isVisible( toolbarView.element ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttoolbarView.focus();\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Provides an integration between {@link #viewportOffset} and {@link module:utils/dom/scroll~scrollViewportToShowTarget}.\n\t * It allows the UI-agnostic engine method to consider user-configured viewport offsets specific for the integration.\n\t *\n\t * @param evt The `scrollToTheSelection` event info.\n\t * @param data The payload carried by the `scrollToTheSelection` event.\n\t */\n\tprivate _handleScrollToTheSelection(\n\t\tevt: EventInfo<'scrollToTheSelection'>,\n\t\tdata: ViewScrollToTheSelectionEvent[ 'args' ][ 0 ]\n\t): void {\n\t\tconst configuredViewportOffset = {\n\t\t\ttop: 0,\n\t\t\tbottom: 0,\n\t\t\tleft: 0,\n\t\t\tright: 0,\n\t\t\t...this.viewportOffset\n\t\t};\n\n\t\tdata.viewportOffset.top += configuredViewportOffset.top;\n\t\tdata.viewportOffset.bottom += configuredViewportOffset.bottom;\n\t\tdata.viewportOffset.left += configuredViewportOffset.left;\n\t\tdata.viewportOffset.right += configuredViewportOffset.right;\n\t}\n}\n\n/**\n * Fired when the editor UI is ready.\n *\n * Fired before {@link module:engine/controller/datacontroller~DataController#event:ready}.\n *\n * @eventName ~EditorUI#ready\n */\nexport type EditorUIReadyEvent = {\n\tname: 'ready';\n\targs: [];\n};\n\n/**\n * Fired whenever the UI (all related components) should be refreshed.\n *\n * **Note:**: The event is fired after each {@link module:engine/view/document~Document#event:layoutChanged}.\n * It can also be fired manually via the {@link module:ui/editorui/editorui~EditorUI#update} method.\n *\n * @eventName ~EditorUI#update\n */\nexport type EditorUIUpdateEvent = {\n\tname: 'update';\n\targs: [];\n};\n\n/**\n * A definition of a focusable toolbar. Used by {@link module:ui/editorui/editorui~EditorUI#addToolbar}.\n */\nexport interface FocusableToolbarDefinition {\n\n\t/**\n\t * An instance of a focusable toolbar view.\n\t */\n\ttoolbarView: ToolbarView;\n\n\t/**\n\t * Options of a focusable toolbar view:\n\t *\n\t * * `isContextual`: Marks the higher priority toolbar. For example when there are 2 visible toolbars,\n\t * it allows to distinguish which toolbar should be focused first after the `alt+f10` keystroke\n\t * * `beforeFocus`: A callback executed before the `ToolbarView` gains focus upon the `Alt+F10` keystroke.\n\t * * `afterBlur`: A callback executed after `ToolbarView` loses focus upon `Esc` keystroke but before\n\t * the focus goes back to the `origin`.\n\t */\n\toptions: FocusableToolbarOptions;\n}\n\nexport interface FocusableToolbarOptions {\n\n\t/**\n\t * Set `true` if the toolbar is attached to the content of the editor. Such toolbar takes\n\t * a precedence over other toolbars when a user pressed Alt + F10.\n\t */\n\tisContextual?: boolean;\n\n\t/**\n\t * Specify a callback executed before the toolbar instance DOM element gains focus\n\t * upon the Alt + F10 keystroke.\n\t */\n\tbeforeFocus?: () => void;\n\n\t/**\n\t * Specify a callback executed after the toolbar instance DOM element loses focus upon\n\t * Esc keystroke but before the focus goes back to the {@link ~EditorUI#setEditableElement editable element}.\n\t */\n\tafterBlur?: () => void;\n}\n\n/**\n * Returns a number (weight) for a toolbar definition. Visible toolbars have a higher priority and so do\n * contextual toolbars (displayed in the context of a content, for instance, an image toolbar).\n *\n * A standard invisible toolbar is the heaviest. A visible contextual toolbar is the lightest.\n *\n * @param toolbarDef A toolbar definition to be weighted.\n */\nfunction getToolbarDefinitionWeight( toolbarDef: FocusableToolbarDefinition ): number {\n\tconst { toolbarView, options } = toolbarDef;\n\tlet weight = 10;\n\n\t// Prioritize already visible toolbars. They should get focused first.\n\tif ( isVisible( toolbarView.element ) ) {\n\t\tweight--;\n\t}\n\n\t// Prioritize contextual toolbars. They are displayed at the selection.\n\tif ( options.isContextual ) {\n\t\tweight--;\n\t}\n\n\treturn weight;\n}\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./editorui.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/editorui/editoruiview\n */\n\nimport View from '../view.js';\nimport BodyCollection from './bodycollection.js';\nimport type EditableUIView from '../editableui/editableuiview.js';\n\nimport type { Locale, LocaleTranslate } from '@ckeditor/ckeditor5-utils';\n\nimport '../../theme/components/editorui/editorui.css';\n\n/**\n * The editor UI view class. Base class for the editor main views.\n */\nexport default abstract class EditorUIView extends View {\n\t/**\n\t * Collection of the child views, detached from the DOM\n\t * structure of the editor, like panels, icons etc.\n\t */\n\tpublic readonly body: BodyCollection;\n\n\tdeclare public locale: Locale;\n\tdeclare public t: LocaleTranslate;\n\n\tpublic abstract get editable(): EditableUIView;\n\n\t/**\n\t * Creates an instance of the editor UI view class.\n\t *\n\t * @param locale The locale instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tthis.body = new BodyCollection( locale );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tthis.body.attachToDom();\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override destroy(): void {\n\t\tthis.body.detachFromDom();\n\n\t\treturn super.destroy();\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/editorui/boxed/boxededitoruiview\n */\n\nimport EditorUIView from '../editoruiview.js';\nimport LabelView from '../../label/labelview.js';\nimport type ViewCollection from '../../viewcollection.js';\n\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\n\n/**\n * The boxed editor UI view class. This class represents an editor interface\n * consisting of a toolbar and an editable area, enclosed within a box.\n */\nexport default abstract class BoxedEditorUIView extends EditorUIView {\n\t/**\n\t * Collection of the child views located in the top (`.ck-editor__top`)\n\t * area of the UI.\n\t */\n\tpublic readonly top: ViewCollection;\n\n\t/**\n\t * Collection of the child views located in the main (`.ck-editor__main`)\n\t * area of the UI.\n\t */\n\tpublic readonly main: ViewCollection;\n\n\t/**\n\t * Voice label of the UI.\n\t */\n\tprivate readonly _voiceLabelView: LabelView;\n\n\t/**\n\t * Creates an instance of the boxed editor UI view class.\n\t *\n\t * @param locale The locale instance..\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tthis.top = this.createCollection();\n\t\tthis.main = this.createCollection();\n\t\tthis._voiceLabelView = this._createVoiceLabel();\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-reset',\n\t\t\t\t\t'ck-editor',\n\t\t\t\t\t'ck-rounded-corners'\n\t\t\t\t],\n\t\t\t\trole: 'application',\n\t\t\t\tdir: locale.uiLanguageDirection,\n\t\t\t\tlang: locale.uiLanguage,\n\t\t\t\t'aria-labelledby': this._voiceLabelView.id\n\t\t\t},\n\n\t\t\tchildren: [\n\t\t\t\tthis._voiceLabelView,\n\t\t\t\t{\n\t\t\t\t\ttag: 'div',\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tclass: [\n\t\t\t\t\t\t\t'ck',\n\t\t\t\t\t\t\t'ck-editor__top',\n\t\t\t\t\t\t\t'ck-reset_all'\n\t\t\t\t\t\t],\n\t\t\t\t\t\trole: 'presentation'\n\t\t\t\t\t},\n\t\t\t\t\tchildren: this.top\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttag: 'div',\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tclass: [\n\t\t\t\t\t\t\t'ck',\n\t\t\t\t\t\t\t'ck-editor__main'\n\t\t\t\t\t\t],\n\t\t\t\t\t\trole: 'presentation'\n\t\t\t\t\t},\n\t\t\t\t\tchildren: this.main\n\t\t\t\t}\n\t\t\t]\n\t\t} );\n\t}\n\n\t/**\n\t * Creates a voice label view instance.\n\t */\n\tprivate _createVoiceLabel() {\n\t\tconst t = this.t;\n\t\tconst voiceLabel = new LabelView();\n\n\t\tvoiceLabel.text = t( 'Rich Text Editor' );\n\n\t\tvoiceLabel.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\tclass: 'ck-voice-label'\n\t\t\t}\n\t\t} );\n\n\t\treturn voiceLabel;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/editableui/editableuiview\n */\n\nimport View from '../view.js';\n\nimport type { EditingView } from '@ckeditor/ckeditor5-engine';\nimport type { Locale, ObservableChangeEvent } from '@ckeditor/ckeditor5-utils';\n\n/**\n * The editable UI view class.\n */\nexport default class EditableUIView extends View {\n\t/**\n\t * The name of the editable UI view.\n\t */\n\tpublic name: string | null = null;\n\n\t/**\n\t * Controls whether the editable is focused, i.e. the user is typing in it.\n\t *\n\t * @observable\n\t */\n\tdeclare public isFocused: boolean;\n\n\t/**\n\t * The editing view instance the editable is related to. Editable uses the editing\n\t * view to dynamically modify its certain DOM attributes after {@link #render rendering}.\n\t *\n\t * **Note**: The DOM attributes are performed by the editing view and not UI\n\t * {@link module:ui/view~View#bindTemplate template bindings} because once rendered,\n\t * the editable DOM element must remain under the full control of the engine to work properly.\n\t */\n\tprotected _editingView: EditingView;\n\n\t/**\n\t * The element which is the main editable element (usually the one with `contentEditable=\"true\"`).\n\t */\n\tprivate _editableElement: HTMLElement | null | undefined;\n\n\t/**\n\t * Whether an external {@link #_editableElement} was passed into the constructor, which also means\n\t * the view will not render its {@link #template}.\n\t */\n\tprivate _hasExternalElement: boolean;\n\n\t/**\n\t * Creates an instance of EditableUIView class.\n\t *\n\t * @param locale The locale instance.\n\t * @param editingView The editing view instance the editable is related to.\n\t * @param editableElement The editable element. If not specified, this view\n\t * should create it. Otherwise, the existing element should be used.\n\t */\n\tconstructor(\n\t\tlocale: Locale,\n\t\teditingView: EditingView,\n\t\teditableElement?: HTMLElement\n\t) {\n\t\tsuper( locale );\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-content',\n\t\t\t\t\t'ck-editor__editable',\n\t\t\t\t\t'ck-rounded-corners'\n\t\t\t\t],\n\t\t\t\tlang: locale.contentLanguage,\n\t\t\t\tdir: locale.contentLanguageDirection\n\t\t\t}\n\t\t} );\n\n\t\tthis.set( 'isFocused', false );\n\n\t\tthis._editableElement = editableElement;\n\t\tthis._hasExternalElement = !!this._editableElement;\n\t\tthis._editingView = editingView;\n\t}\n\n\t/**\n\t * Renders the view by either applying the {@link #template} to the existing\n\t * {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement} or assigning {@link #element}\n\t * as {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement}.\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tif ( this._hasExternalElement ) {\n\t\t\tthis.template!.apply( this.element = this._editableElement! );\n\t\t} else {\n\t\t\tthis._editableElement = this.element;\n\t\t}\n\n\t\tthis.on( 'change:isFocused', () => this._updateIsFocusedClasses() );\n\t\tthis._updateIsFocusedClasses();\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override destroy(): void {\n\t\tif ( this._hasExternalElement ) {\n\t\t\tthis.template!.revert( this._editableElement! );\n\t\t}\n\n\t\tsuper.destroy();\n\t}\n\n\t/**\n\t * Whether an external {@link #_editableElement} was passed into the constructor, which also means\n\t * the view will not render its {@link #template}.\n\t */\n\tpublic get hasExternalElement(): boolean {\n\t\treturn this._hasExternalElement;\n\t}\n\n\t/**\n\t * Updates the `ck-focused` and `ck-blurred` CSS classes on the {@link #element} according to\n\t * the {@link #isFocused} property value using the {@link #_editingView editing view} API.\n\t */\n\tprivate _updateIsFocusedClasses() {\n\t\tconst editingView = this._editingView;\n\n\t\tif ( editingView.isRenderingInProgress ) {\n\t\t\tupdateAfterRender( this );\n\t\t} else {\n\t\t\tupdate( this );\n\t\t}\n\n\t\tfunction update( view: EditableUIView ) {\n\t\t\teditingView.change( writer => {\n\t\t\t\tconst viewRoot = editingView.document.getRoot( view.name! )!;\n\n\t\t\t\twriter.addClass( view.isFocused ? 'ck-focused' : 'ck-blurred', viewRoot );\n\t\t\t\twriter.removeClass( view.isFocused ? 'ck-blurred' : 'ck-focused', viewRoot );\n\t\t\t} );\n\t\t}\n\n\t\t// In a case of a multi-root editor, a callback will be attached more than once (one callback for each root).\n\t\t// While executing one callback the `isRenderingInProgress` observable is changing what causes executing another\n\t\t// callback and render is called inside the already pending render.\n\t\t// We need to be sure that callback is executed only when the value has changed from `true` to `false`.\n\t\t// See https://github.com/ckeditor/ckeditor5/issues/1676.\n\t\tfunction updateAfterRender( view: EditableUIView ) {\n\t\t\teditingView.once>( 'change:isRenderingInProgress', ( evt, name, value ) => {\n\t\t\t\tif ( !value ) {\n\t\t\t\t\tupdate( view );\n\t\t\t\t} else {\n\t\t\t\t\tupdateAfterRender( view );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/editableui/inline/inlineeditableuiview\n */\n\nimport EditableUIView from '../editableuiview.js';\n\nimport type { EditingView } from '@ckeditor/ckeditor5-engine';\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\n\n/**\n * The inline editable UI class implementing an inline {@link module:ui/editableui/editableuiview~EditableUIView}.\n */\nexport default class InlineEditableUIView extends EditableUIView {\n\t/**\n\t * A function that gets called with the instance of this view as an argument and should return a string that\n\t * represents the label of the editable for assistive technologies.\n\t */\n\tprivate readonly _generateLabel: ( view: InlineEditableUIView ) => string;\n\n\t/**\n\t * Creates an instance of the InlineEditableUIView class.\n\t *\n\t * @param locale The locale instance.\n\t * @param editingView The editing view instance the editable is related to.\n\t * @param editableElement The editable element. If not specified, the\n\t * {@link module:ui/editableui/editableuiview~EditableUIView}\n\t * will create it. Otherwise, the existing element will be used.\n\t * @param options Additional configuration of the view.\n\t * @param options.label A function that gets called with the instance of this view as an argument\n\t * and should return a string that represents the label of the editable for assistive technologies. If not provided,\n\t * a default label generator is used.\n\t */\n\tconstructor(\n\t\tlocale: Locale,\n\t\teditingView: EditingView,\n\t\teditableElement?: HTMLElement,\n\t\toptions: { label?: ( view: InlineEditableUIView ) => string } = {}\n\t) {\n\t\tsuper( locale, editingView, editableElement );\n\n\t\tconst t = locale.t;\n\n\t\tthis.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\trole: 'textbox',\n\t\t\t\tclass: 'ck-editor__editable_inline'\n\t\t\t}\n\t\t} );\n\n\t\tthis._generateLabel = options.label || ( () => t( 'Editor editing area: %0', this.name! ) );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tconst editingView = this._editingView;\n\n\t\teditingView.change( writer => {\n\t\t\tconst viewRoot = editingView.document.getRoot( this.name! );\n\n\t\t\twriter.setAttribute( 'aria-label', this._generateLabel( this ), viewRoot! );\n\t\t} );\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/model\n */\n\nimport { ObservableMixin } from '@ckeditor/ckeditor5-utils';\nimport { extend } from 'lodash-es';\n\n/**\n * The base MVC model class.\n */\nexport default class Model extends /* #__PURE__ */ ObservableMixin() {\n\t[ x: string ]: unknown;\n\n\t/**\n\t * Creates a new Model instance.\n\t *\n\t * @param attributes The model state attributes to be defined during the instance creation.\n\t * @param properties The (out of state) properties to be appended to the instance during creation.\n\t */\n\tconstructor( attributes?: Record, properties?: Record ) {\n\t\tsuper();\n\n\t\t// Extend this instance with the additional (out of state) properties.\n\t\tif ( properties ) {\n\t\t\textend( this, properties );\n\t\t}\n\n\t\t// Initialize the attributes.\n\t\tif ( attributes ) {\n\t\t\tthis.set( attributes );\n\t\t}\n\t}\n}\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./balloonrotator.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./fakepanel.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/panel/balloon/contextualballoon\n */\n\nimport BalloonPanelView from './balloonpanelview.js';\nimport View from '../../view.js';\nimport ButtonView from '../../button/buttonview.js';\nimport type { ButtonExecuteEvent } from '../../button/button.js';\nimport type ViewCollection from '../../viewcollection.js';\n\nimport { Plugin, icons, type Editor } from '@ckeditor/ckeditor5-core';\nimport {\n\tCKEditorError,\n\tFocusTracker,\n\tRect,\n\ttoUnit,\n\ttype Locale,\n\ttype ObservableChangeEvent,\n\ttype PositionOptions\n} from '@ckeditor/ckeditor5-utils';\n\nimport '../../../theme/components/panel/balloonrotator.css';\nimport '../../../theme/components/panel/fakepanel.css';\n\nconst toPx = /* #__PURE__ */ toUnit( 'px' );\n\n/**\n * Provides the common contextual balloon for the editor.\n *\n * The role of this plugin is to unify the contextual balloons logic, simplify views management and help\n * avoid the unnecessary complexity of handling multiple {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView}\n * instances in the editor.\n *\n * This plugin allows for creating single or multiple panel stacks.\n *\n * Each stack may have multiple views, with the one on the top being visible. When the visible view is removed from the stack,\n * the previous view becomes visible.\n *\n * It might be useful to implement nested navigation in a balloon. For instance, a toolbar view may contain a link button.\n * When you click it, a link view (which lets you set the URL) is created and put on top of the toolbar view, so the link panel\n * is displayed. When you finish editing the link and close (remove) the link view, the toolbar view is visible again.\n *\n * However, there are cases when there are multiple independent balloons to be displayed, for instance, if the selection\n * is inside two inline comments at the same time. For such cases, you can create two independent panel stacks.\n * The contextual balloon plugin will create a navigation bar to let the users switch between these panel stacks using the \"Next\"\n * and \"Previous\" buttons.\n *\n * If there are no views in the current stack, the balloon panel will try to switch to the next stack. If there are no\n * panels in any stack, the balloon panel will be hidden.\n *\n * **Note**: To force the balloon panel to show only one view, even if there are other stacks, use the `singleViewMode=true` option\n * when {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon#add adding} a view to a panel.\n *\n * From the implementation point of view, the contextual ballon plugin is reusing a single\n * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView} instance to display multiple contextual balloon\n * panels in the editor. It also creates a special {@link module:ui/panel/balloon/contextualballoon~RotatorView rotator view},\n * used to manage multiple panel stacks. Rotator view is a child of the balloon panel view and the parent of the specific\n * view you want to display. If there is more than one panel stack to be displayed, the rotator view will add a\n * navigation bar. If there is only one stack, the rotator view is transparent (it does not add any UI elements).\n */\nexport default class ContextualBalloon extends Plugin {\n\t/**\n\t * The {@link module:utils/dom/position~Options#limiter position limiter}\n\t * for the {@link #view balloon}, used when no `limiter` has been passed into {@link #add}\n\t * or {@link #updatePosition}.\n\t *\n\t * By default, a function that obtains the farthest DOM\n\t * {@link module:engine/view/rooteditableelement~RootEditableElement}\n\t * of the {@link module:engine/view/document~Document#selection}.\n\t */\n\tpublic positionLimiter: PositionOptions[ 'limiter' ];\n\n\tpublic visibleStack?: string;\n\n\t/**\n\t * The currently visible view or `null` when there are no views in any stack.\n\t *\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public visibleView: View | null;\n\n\t/**\n\t * A total number of all stacks in the balloon.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _numberOfStacks: number;\n\n\t/**\n\t * A flag that controls the single view mode.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _singleViewMode: boolean;\n\n\t/**\n\t * The map of views and their stacks.\n\t */\n\tprivate _viewToStack = new Map();\n\n\t/**\n\t * The map of IDs and stacks.\n\t */\n\tprivate _idToStack = new Map();\n\n\t/**\n\t * The common balloon panel view.\n\t */\n\tprivate _view: BalloonPanelView | null = null;\n\n\t/**\n\t * Rotator view embedded in the contextual balloon.\n\t * Displays the currently visible view in the balloon and provides navigation for switching stacks.\n\t */\n\tprivate _rotatorView: RotatorView | null = null;\n\n\t/**\n\t * Displays fake panels under the balloon panel view when multiple stacks are added to the balloon.\n\t */\n\tprivate _fakePanelsView: FakePanelsView | null = null;\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic static get pluginName() {\n\t\treturn 'ContextualBalloon' as const;\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tconstructor( editor: Editor ) {\n\t\tsuper( editor );\n\n\t\tthis.positionLimiter = () => {\n\t\t\tconst view = this.editor.editing.view;\n\t\t\tconst viewDocument = view.document;\n\t\t\tconst editableElement = viewDocument.selection.editableElement;\n\n\t\t\tif ( editableElement ) {\n\t\t\t\treturn view.domConverter.mapViewToDom( editableElement.root ) as HTMLElement;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t};\n\n\t\tthis.set( 'visibleView', null );\n\t\tthis.set( '_numberOfStacks', 0 );\n\t\tthis.set( '_singleViewMode', false );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override destroy(): void {\n\t\tsuper.destroy();\n\n\t\tif ( this._view ) {\n\t\t\tthis._view.destroy();\n\t\t}\n\n\t\tif ( this._rotatorView ) {\n\t\t\tthis._rotatorView.destroy();\n\t\t}\n\n\t\tif ( this._fakePanelsView ) {\n\t\t\tthis._fakePanelsView.destroy();\n\t\t}\n\t}\n\n\t/**\n\t * The common balloon panel view.\n\t */\n\tpublic get view(): BalloonPanelView {\n\t\tif ( !this._view ) {\n\t\t\tthis._createPanelView();\n\t\t}\n\n\t\treturn this._view!;\n\t}\n\n\t/**\n\t * Returns `true` when the given view is in one of the stacks. Otherwise returns `false`.\n\t */\n\tpublic hasView( view: View ): boolean {\n\t\treturn Array.from( this._viewToStack.keys() ).includes( view );\n\t}\n\n\t/**\n\t * Adds a new view to the stack and makes it visible if the current stack is visible\n\t * or it is the first view in the balloon.\n\t *\n\t * @param data The configuration of the view.\n\t * @param data.stackId The ID of the stack that the view is added to. Defaults to `'main'`.\n\t * @param data.view The content of the balloon.\n\t * @param data.position Positioning options.\n\t * @param data.balloonClassName An additional CSS class added to the {@link #view balloon} when visible.\n\t * @param data.withArrow Whether the {@link #view balloon} should be rendered with an arrow. Defaults to `true`.\n\t * @param data.singleViewMode Whether the view should be the only visible view even if other stacks were added. Defaults to `false`.\n\t */\n\tpublic add( data: ViewConfiguration ): void {\n\t\tif ( !this._view ) {\n\t\t\tthis._createPanelView();\n\t\t}\n\n\t\tif ( this.hasView( data.view ) ) {\n\t\t\t/**\n\t\t\t * Trying to add configuration of the same view more than once.\n\t\t\t *\n\t\t\t * @error contextualballoon-add-view-exist\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'contextualballoon-add-view-exist',\n\t\t\t\t[ this, data ]\n\t\t\t);\n\t\t}\n\n\t\tconst stackId = data.stackId || 'main';\n\n\t\t// If new stack is added, creates it and show view from this stack.\n\t\tif ( !this._idToStack.has( stackId ) ) {\n\t\t\tthis._idToStack.set( stackId, new Map( [ [ data.view, data ] ] ) );\n\t\t\tthis._viewToStack.set( data.view, this._idToStack.get( stackId )! );\n\t\t\tthis._numberOfStacks = this._idToStack.size;\n\n\t\t\tif ( !this._visibleStack || data.singleViewMode ) {\n\t\t\t\tthis.showStack( stackId );\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst stack = this._idToStack.get( stackId )!;\n\n\t\tif ( data.singleViewMode ) {\n\t\t\tthis.showStack( stackId );\n\t\t}\n\n\t\t// Add new view to the stack.\n\t\tstack.set( data.view, data );\n\t\tthis._viewToStack.set( data.view, stack );\n\n\t\t// And display it if is added to the currently visible stack.\n\t\tif ( stack === this._visibleStack ) {\n\t\t\tthis._showView( data );\n\t\t}\n\t}\n\n\t/**\n\t * Removes the given view from the stack. If the removed view was visible,\n\t * the view preceding it in the stack will become visible instead.\n\t * When there is no view in the stack, the next stack will be displayed.\n\t * When there are no more stacks, the balloon will hide.\n\t *\n\t * @param view A view to be removed from the balloon.\n\t */\n\tpublic remove( view: View ): void {\n\t\tif ( !this.hasView( view ) ) {\n\t\t\t/**\n\t\t\t * Trying to remove the configuration of the view not defined in the stack.\n\t\t\t *\n\t\t\t * @error contextualballoon-remove-view-not-exist\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'contextualballoon-remove-view-not-exist',\n\t\t\t\t[ this, view ]\n\t\t\t);\n\t\t}\n\n\t\tconst stack = this._viewToStack.get( view )!;\n\n\t\tif ( this._singleViewMode && this.visibleView === view ) {\n\t\t\tthis._singleViewMode = false;\n\t\t}\n\n\t\t// When visible view will be removed we need to show a preceding view or next stack\n\t\t// if a view is the only view in the stack.\n\t\tif ( this.visibleView === view ) {\n\t\t\tif ( stack.size === 1 ) {\n\t\t\t\tif ( this._idToStack.size > 1 ) {\n\t\t\t\t\tthis._showNextStack();\n\t\t\t\t} else {\n\t\t\t\t\tthis.view.hide();\n\t\t\t\t\tthis.visibleView = null;\n\t\t\t\t\tthis._rotatorView!.hideView();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis._showView( Array.from( stack.values() )[ stack.size - 2 ] );\n\t\t\t}\n\t\t}\n\n\t\tif ( stack.size === 1 ) {\n\t\t\tthis._idToStack.delete( this._getStackId( stack ) );\n\t\t\tthis._numberOfStacks = this._idToStack.size;\n\t\t} else {\n\t\t\tstack.delete( view );\n\t\t}\n\n\t\tthis._viewToStack.delete( view );\n\t}\n\n\t/**\n\t * Updates the position of the balloon using the position data of the first visible view in the stack.\n\t * When new position data is given, the position data of the currently visible view will be updated.\n\t *\n\t * @param position Position options.\n\t */\n\tpublic updatePosition( position?: Partial ): void {\n\t\tif ( position ) {\n\t\t\tthis._visibleStack.get( this.visibleView! )!.position = position;\n\t\t}\n\n\t\tthis.view.pin( this._getBalloonPosition()! );\n\t\tthis._fakePanelsView!.updatePosition();\n\t}\n\n\t/**\n\t * Shows the last view from the stack of a given ID.\n\t */\n\tpublic showStack( id: string ): void {\n\t\tthis.visibleStack = id;\n\t\tconst stack = this._idToStack.get( id );\n\n\t\tif ( !stack ) {\n\t\t\t/**\n\t\t\t * Trying to show a stack that does not exist.\n\t\t\t *\n\t\t\t * @error contextualballoon-showstack-stack-not-exist\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'contextualballoon-showstack-stack-not-exist',\n\t\t\t\tthis\n\t\t\t);\n\t\t}\n\n\t\tif ( this._visibleStack === stack ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._showView( Array.from( stack.values() ).pop()! );\n\t}\n\n\t/**\n\t * Initializes view instances.\n\t */\n\tprivate _createPanelView(): void {\n\t\tthis._view = new BalloonPanelView( this.editor.locale );\n\n\t\tthis.editor.ui.view.body.add( this._view );\n\t\tthis.editor.ui.focusTracker.add( this._view.element! );\n\n\t\tthis._rotatorView = this._createRotatorView();\n\t\tthis._fakePanelsView = this._createFakePanelsView();\n\t}\n\n\t/**\n\t * Returns the stack of the currently visible view.\n\t */\n\tprivate get _visibleStack() {\n\t\treturn this._viewToStack.get( this.visibleView! )!;\n\t}\n\n\t/**\n\t * Returns the ID of the given stack.\n\t */\n\tprivate _getStackId( stack: Stack ) {\n\t\tconst entry = Array.from( this._idToStack.entries() ).find( entry => entry[ 1 ] === stack )!;\n\n\t\treturn entry[ 0 ];\n\t}\n\n\t/**\n\t * Shows the last view from the next stack.\n\t */\n\tprivate _showNextStack() {\n\t\tconst stacks = Array.from( this._idToStack.values() );\n\n\t\tlet nextIndex = stacks.indexOf( this._visibleStack ) + 1;\n\n\t\tif ( !stacks[ nextIndex ] ) {\n\t\t\tnextIndex = 0;\n\t\t}\n\n\t\tthis.showStack( this._getStackId( stacks[ nextIndex ] ) );\n\t}\n\n\t/**\n\t * Shows the last view from the previous stack.\n\t */\n\tprivate _showPrevStack() {\n\t\tconst stacks = Array.from( this._idToStack.values() );\n\n\t\tlet nextIndex = stacks.indexOf( this._visibleStack ) - 1;\n\n\t\tif ( !stacks[ nextIndex ] ) {\n\t\t\tnextIndex = stacks.length - 1;\n\t\t}\n\n\t\tthis.showStack( this._getStackId( stacks[ nextIndex ] ) );\n\t}\n\n\t/**\n\t * Creates a rotator view.\n\t */\n\tprivate _createRotatorView() {\n\t\tconst view = new RotatorView( this.editor.locale );\n\t\tconst t = this.editor.locale.t;\n\n\t\tthis.view.content.add( view );\n\n\t\t// Hide navigation when there is only a one stack & not in single view mode.\n\t\tview.bind( 'isNavigationVisible' ).to( this, '_numberOfStacks', this, '_singleViewMode', ( value, isSingleViewMode ) => {\n\t\t\treturn !isSingleViewMode && value > 1;\n\t\t} );\n\n\t\t// Update balloon position after toggling navigation.\n\t\tview.on( 'change:isNavigationVisible', () => ( this.updatePosition() ), { priority: 'low' } );\n\n\t\t// Update stacks counter value.\n\t\tview.bind( 'counter' ).to( this, 'visibleView', this, '_numberOfStacks', ( visibleView, numberOfStacks ) => {\n\t\t\tif ( numberOfStacks < 2 ) {\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\tconst current = Array.from( this._idToStack.values() ).indexOf( this._visibleStack ) + 1;\n\n\t\t\treturn t( '%0 of %1', [ current, numberOfStacks ] );\n\t\t} );\n\n\t\tview.buttonNextView.on( 'execute', () => {\n\t\t\t// When current view has a focus then move focus to the editable before removing it,\n\t\t\t// otherwise editor will lost focus.\n\t\t\tif ( view.focusTracker.isFocused ) {\n\t\t\t\tthis.editor.editing.view.focus();\n\t\t\t}\n\n\t\t\tthis._showNextStack();\n\t\t} );\n\n\t\tview.buttonPrevView.on( 'execute', () => {\n\t\t\t// When current view has a focus then move focus to the editable before removing it,\n\t\t\t// otherwise editor will lost focus.\n\t\t\tif ( view.focusTracker.isFocused ) {\n\t\t\t\tthis.editor.editing.view.focus();\n\t\t\t}\n\n\t\t\tthis._showPrevStack();\n\t\t} );\n\n\t\treturn view;\n\t}\n\n\t/**\n\t * Creates a fake panels view.\n\t */\n\tprivate _createFakePanelsView() {\n\t\tconst view = new FakePanelsView( this.editor.locale, this.view );\n\n\t\tview.bind( 'numberOfPanels' ).to( this, '_numberOfStacks', this, '_singleViewMode', ( number, isSingleViewMode ) => {\n\t\t\tconst showPanels = !isSingleViewMode && number >= 2;\n\n\t\t\treturn showPanels ? Math.min( number - 1, 2 ) : 0;\n\t\t} );\n\n\t\tview.listenTo( this.view, 'change:top', () => view.updatePosition() );\n\t\tview.listenTo( this.view, 'change:left', () => view.updatePosition() );\n\n\t\tthis.editor.ui.view.body.add( view );\n\n\t\treturn view;\n\t}\n\n\t/**\n\t * Sets the view as the content of the balloon and attaches the balloon using position\n\t * options of the first view.\n\t *\n\t * @param data Configuration.\n\t * @param data.view The view to show in the balloon.\n\t * @param data.balloonClassName Additional class name which will be added to the {@link #view balloon}.\n\t * @param data.withArrow Whether the {@link #view balloon} should be rendered with an arrow.\n\t */\n\tprivate _showView( { view, balloonClassName = '', withArrow = true, singleViewMode = false }: ViewConfiguration ) {\n\t\tthis.view.class = balloonClassName;\n\t\tthis.view.withArrow = withArrow;\n\n\t\tthis._rotatorView!.showView( view );\n\t\tthis.visibleView = view;\n\t\tthis.view.pin( this._getBalloonPosition()! );\n\t\tthis._fakePanelsView!.updatePosition();\n\n\t\tif ( singleViewMode ) {\n\t\t\tthis._singleViewMode = true;\n\t\t}\n\t}\n\n\t/**\n\t * Returns position options of the last view in the stack.\n\t * This keeps the balloon in the same position when the view is changed.\n\t */\n\tprivate _getBalloonPosition() {\n\t\tlet position = Array.from( this._visibleStack.values() ).pop()!.position;\n\n\t\tif ( position ) {\n\t\t\t// Use the default limiter if none has been specified.\n\t\t\tif ( !position.limiter ) {\n\t\t\t\t// Don't modify the original options object.\n\t\t\t\tposition = Object.assign( {}, position, {\n\t\t\t\t\tlimiter: this.positionLimiter\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\t// Don't modify the original options object.\n\t\t\tposition = Object.assign( {}, position, {\n\t\t\t\tviewportOffsetConfig: this.editor.ui.viewportOffset\n\t\t\t} );\n\t\t}\n\n\t\treturn position;\n\t}\n}\n\n/**\n * The configuration of the view.\n */\nexport interface ViewConfiguration {\n\n\t/**\n\t * The ID of the stack that the view is added to.\n\t *\n\t * @default 'main'\n\t */\n\tstackId?: string;\n\n\t/**\n\t * The content of the balloon.\n\t */\n\tview: View;\n\n\t/**\n\t * Positioning options.\n\t */\n\tposition?: Partial;\n\n\t/**\n\t * An additional CSS class added to the {@link #view balloon} when visible.\n\t */\n\tballoonClassName?: string;\n\n\t/**\n\t * Whether the {@link #view balloon} should be rendered with an arrow.\n\t *\n\t * @default true\n\t */\n\twithArrow?: boolean;\n\n\t/**\n\t * Whether the view should be the only visible view even if other stacks were added.\n\t *\n\t * @default false\n\t */\n\tsingleViewMode?: boolean;\n}\n\ntype Stack = Map;\n\n/**\n * Rotator view is a helper class for the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon ContextualBalloon}.\n * It is used for displaying the last view from the current stack and providing navigation buttons for switching stacks.\n * See the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon ContextualBalloon} documentation to learn more.\n */\nexport class RotatorView extends View {\n\t/**\n\t * Used for checking if a view is focused or not.\n\t */\n\tpublic readonly focusTracker: FocusTracker;\n\n\t/**\n\t * Navigation button for switching the stack to the previous one.\n\t */\n\tpublic readonly buttonPrevView: ButtonView;\n\n\t/**\n\t * Navigation button for switching the stack to the next one.\n\t */\n\tpublic readonly buttonNextView: ButtonView;\n\n\t/**\n\t * A collection of the child views that creates the rotator content.\n\t */\n\tpublic readonly content: ViewCollection;\n\n\t/**\n\t * Defines whether navigation is visible or not.\n\t *\n\t * @observable\n\t */\n\tdeclare public isNavigationVisible: boolean;\n\n\t/**\n\t * @observable\n\t */\n\tdeclare public counter: string;\n\n\t/**\n\t * @inheritDoc\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst t = locale.t;\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.set( 'isNavigationVisible', true );\n\n\t\tthis.focusTracker = new FocusTracker();\n\t\tthis.buttonPrevView = this._createButtonView( t( 'Previous' ), icons.previousArrow );\n\t\tthis.buttonNextView = this._createButtonView( t( 'Next' ), icons.nextArrow );\n\t\tthis.content = this.createCollection();\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-balloon-rotator'\n\t\t\t\t],\n\t\t\t\t'z-index': '-1'\n\t\t\t},\n\t\t\tchildren: [\n\t\t\t\t{\n\t\t\t\t\ttag: 'div',\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tclass: [\n\t\t\t\t\t\t\t'ck-balloon-rotator__navigation',\n\t\t\t\t\t\t\tbind.to( 'isNavigationVisible', value => value ? '' : 'ck-hidden' )\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\tthis.buttonPrevView,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttag: 'span',\n\n\t\t\t\t\t\t\tattributes: {\n\t\t\t\t\t\t\t\tclass: [\n\t\t\t\t\t\t\t\t\t'ck-balloon-rotator__counter'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t},\n\n\t\t\t\t\t\t\tchildren: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttext: bind.to( 'counter' )\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t},\n\t\t\t\t\t\tthis.buttonNextView\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttag: 'div',\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\tclass: 'ck-balloon-rotator__content'\n\t\t\t\t\t},\n\t\t\t\t\tchildren: this.content\n\t\t\t\t}\n\t\t\t]\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tthis.focusTracker.add( this.element! );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override destroy(): void {\n\t\tsuper.destroy();\n\n\t\tthis.focusTracker.destroy();\n\t}\n\n\t/**\n\t * Shows a given view.\n\t *\n\t * @param view The view to show.\n\t */\n\tpublic showView( view: View ): void {\n\t\tthis.hideView();\n\t\tthis.content.add( view );\n\t}\n\n\t/**\n\t * Hides the currently displayed view.\n\t */\n\tpublic hideView(): void {\n\t\tthis.content.clear();\n\t}\n\n\t/**\n\t * Creates a navigation button view.\n\t *\n\t * @param label The button label.\n\t * @param icon The button icon.\n\t */\n\tprivate _createButtonView( label: string, icon: string ) {\n\t\tconst view = new ButtonView( this.locale );\n\n\t\tview.set( {\n\t\t\tlabel,\n\t\t\ticon,\n\t\t\ttooltip: true\n\t\t} );\n\n\t\treturn view;\n\t}\n}\n\n/**\n * Displays additional layers under the balloon when multiple stacks are added to the balloon.\n */\nclass FakePanelsView extends View {\n\t/**\n\t * Collection of the child views which creates fake panel content.\n\t */\n\tpublic readonly content: ViewCollection;\n\n\t/**\n\t * Fake panels top offset.\n\t *\n\t * @observable\n\t */\n\tdeclare public top: number;\n\n\t/**\n\t * Fake panels left offset.\n\t *\n\t * @observable\n\t */\n\tdeclare public left: number;\n\n\t/**\n\t * Fake panels height.\n\t *\n\t * @observable\n\t */\n\tdeclare public height: number;\n\n\t/**\n\t * Fake panels width.\n\t *\n\t * @observable\n\t */\n\tdeclare public width: number;\n\n\t/**\n\t * Number of rendered fake panels.\n\t *\n\t * @observable\n\t */\n\tdeclare public numberOfPanels: number;\n\n\t/**\n\t * Context.\n\t */\n\tprivate readonly _balloonPanelView: BalloonPanelView;\n\n\t/**\n\t * @inheritDoc\n\t */\n\tconstructor( locale: Locale, balloonPanelView: BalloonPanelView ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.set( 'top', 0 );\n\t\tthis.set( 'left', 0 );\n\t\tthis.set( 'height', 0 );\n\t\tthis.set( 'width', 0 );\n\t\tthis.set( 'numberOfPanels', 0 );\n\n\t\tthis.content = this.createCollection();\n\t\tthis._balloonPanelView = balloonPanelView;\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck-fake-panel',\n\t\t\t\t\tbind.to( 'numberOfPanels', number => number ? '' : 'ck-hidden' )\n\t\t\t\t],\n\t\t\t\tstyle: {\n\t\t\t\t\ttop: bind.to( 'top', toPx ),\n\t\t\t\t\tleft: bind.to( 'left', toPx ),\n\t\t\t\t\twidth: bind.to( 'width', toPx ),\n\t\t\t\t\theight: bind.to( 'height', toPx )\n\t\t\t\t}\n\t\t\t},\n\t\t\tchildren: this.content\n\t\t} );\n\n\t\tthis.on>( 'change:numberOfPanels', ( evt, name, next, prev ) => {\n\t\t\tif ( next > prev ) {\n\t\t\t\tthis._addPanels( next - prev );\n\t\t\t} else {\n\t\t\t\tthis._removePanels( prev - next );\n\t\t\t}\n\n\t\t\tthis.updatePosition();\n\t\t} );\n\t}\n\n\tprivate _addPanels( number: number ) {\n\t\twhile ( number-- ) {\n\t\t\tconst view = new View();\n\n\t\t\tview.setTemplate( { tag: 'div' } );\n\n\t\t\tthis.content.add( view );\n\t\t\tthis.registerChild( view );\n\t\t}\n\t}\n\n\tprivate _removePanels( number: number ) {\n\t\twhile ( number-- ) {\n\t\t\tconst view = this.content.last!;\n\n\t\t\tthis.content.remove( view );\n\t\t\tthis.deregisterChild( view );\n\t\t\tview.destroy();\n\t\t}\n\t}\n\n\t/**\n\t * Updates coordinates of fake panels.\n\t */\n\tpublic updatePosition() {\n\t\tif ( this.numberOfPanels ) {\n\t\t\tconst { top, left } = this._balloonPanelView;\n\t\t\tconst { width, height } = new Rect( this._balloonPanelView.element! );\n\n\t\t\tObject.assign( this, { top, left, width, height } );\n\t\t}\n\t}\n}\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./stickypanel.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/panel/sticky/stickypanelview\n */\n\nimport View from '../../view.js';\nimport Template from '../../template.js';\n\nimport type ViewCollection from '../../viewcollection.js';\n\nimport {\n\ttype Locale,\n\ttype ObservableChangeEvent,\n\tglobal,\n\ttoUnit,\n\tRect\n} from '@ckeditor/ckeditor5-utils';\n\n// @if CK_DEBUG_STICKYPANEL // const {\n// @if CK_DEBUG_STICKYPANEL // \tdefault: RectDrawer,\n// @if CK_DEBUG_STICKYPANEL // \tdiagonalStylesBlack\n// @if CK_DEBUG_STICKYPANEL // } = require( '@ckeditor/ckeditor5-utils/tests/_utils/rectdrawer' );\n\nimport '../../../theme/components/panel/stickypanel.css';\n\nconst toPx = /* #__PURE__ */ toUnit( 'px' );\n\n/**\n * The sticky panel view class.\n */\nexport default class StickyPanelView extends View {\n\t/**\n\t * Collection of the child views which creates balloon panel contents.\n\t */\n\tpublic readonly content: ViewCollection;\n\n\t/**\n\t * Controls whether the sticky panel should be active.\n\t *\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public isActive: boolean;\n\n\t/**\n\t * Controls whether the sticky panel is in the \"sticky\" state.\n\t *\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public isSticky: boolean;\n\n\t/**\n\t * The limiter element for the sticky panel instance. Its bounding rect limits\n\t * the \"stickyness\" of the panel, i.e. when the panel reaches the bottom\n\t * edge of the limiter, it becomes sticky to that edge and does not float\n\t * off the limiter. It is mandatory for the panel to work properly and once\n\t * set, it cannot be changed.\n\t *\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public limiterElement: HTMLElement | null;\n\n\t/**\n\t * The offset from the bottom edge of {@link #limiterElement}\n\t * which stops the panel from stickying any further to prevent limiter's content\n\t * from being completely covered.\n\t *\n\t * @readonly\n\t * @observable\n\t * @default 50\n\t */\n\tdeclare public limiterBottomOffset: number;\n\n\t/**\n\t * The offset from the top edge of the web browser's viewport which makes the\n\t * panel become sticky. The default value is `0`, which means the panel becomes\n\t * sticky when it's upper edge touches the top of the page viewport.\n\t *\n\t * This attribute is useful when the web page has UI elements positioned to the top\n\t * either using `position: fixed` or `position: sticky`, which would cover the\n\t * sticky panel or vice–versa (depending on the `z-index` hierarchy).\n\t *\n\t * Bound to {@link module:ui/editorui/editorui~EditorUI#viewportOffset `EditorUI#viewportOffset`}.\n\t *\n\t * If {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset.top`} is defined, then\n\t * it will override the default value.\n\t *\n\t * @observable\n\t * @default 0\n\t */\n\tdeclare public viewportTopOffset: number;\n\n\t/**\n\t * The panel which accepts children into {@link #content} collection.\n\t * Also an element which is positioned when {@link #isSticky}.\n\t */\n\tpublic contentPanelElement: HTMLElement;\n\n\t/**\n\t * Controls the `margin-left` CSS style of the panel.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _marginLeft: string | null;\n\n\t/**\n\t * Set `true` if the sticky panel reached the bottom edge of the\n\t * {@link #limiterElement}.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _isStickyToTheBottomOfLimiter: boolean;\n\n\t/**\n\t * The `top` CSS position of the panel when it is sticky to the top of the viewport or scrollable\n\t * ancestors of the {@link #limiterElement}.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _stickyTopOffset: number | null;\n\n\t/**\n\t * The `bottom` CSS position of the panel when it is sticky to the bottom of the {@link #limiterElement}.\n\t *\n\t * @private\n\t * @readonly\n\t * @observable\n\t */\n\tdeclare public _stickyBottomOffset: number | null;\n\n\t/**\n\t * A dummy element which visually fills the space as long as the\n\t * actual panel is sticky. It prevents flickering of the UI.\n\t */\n\tprivate _contentPanelPlaceholder: HTMLElement;\n\n\t/**\n\t * @inheritDoc\n\t */\n\tconstructor( locale?: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.set( 'isActive', false );\n\t\tthis.set( 'isSticky', false );\n\t\tthis.set( 'limiterElement', null );\n\t\tthis.set( 'limiterBottomOffset', 50 );\n\t\tthis.set( 'viewportTopOffset', 0 );\n\n\t\tthis.set( '_marginLeft', null );\n\t\tthis.set( '_isStickyToTheBottomOfLimiter', false );\n\n\t\tthis.set( '_stickyTopOffset', null );\n\t\tthis.set( '_stickyBottomOffset', null );\n\n\t\tthis.content = this.createCollection();\n\n\t\tthis._contentPanelPlaceholder = new Template( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-sticky-panel__placeholder'\n\t\t\t\t],\n\t\t\t\tstyle: {\n\t\t\t\t\tdisplay: bind.to( 'isSticky', isSticky => isSticky ? 'block' : 'none' ),\n\t\t\t\t\theight: bind.to( 'isSticky', isSticky => {\n\t\t\t\t\t\treturn isSticky ? toPx( this._contentPanelRect.height ) : null;\n\t\t\t\t\t} )\n\t\t\t\t}\n\t\t\t}\n\t\t} ).render() as HTMLElement;\n\n\t\tthis.contentPanelElement = new Template( {\n\t\t\ttag: 'div',\n\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-sticky-panel__content',\n\t\t\t\t\t// Toggle class of the panel when \"sticky\" state changes in the view.\n\t\t\t\t\tbind.if( 'isSticky', 'ck-sticky-panel__content_sticky' ),\n\t\t\t\t\tbind.if( '_isStickyToTheBottomOfLimiter', 'ck-sticky-panel__content_sticky_bottom-limit' )\n\t\t\t\t],\n\t\t\t\tstyle: {\n\t\t\t\t\twidth: bind.to( 'isSticky', isSticky => {\n\t\t\t\t\t\treturn isSticky ? toPx( this._contentPanelPlaceholder.getBoundingClientRect().width ) : null;\n\t\t\t\t\t} ),\n\n\t\t\t\t\ttop: bind.to( '_stickyTopOffset', value => value ? toPx( value ) : value ),\n\t\t\t\t\tbottom: bind.to( '_stickyBottomOffset', value => value ? toPx( value ) : value ),\n\n\t\t\t\t\tmarginLeft: bind.to( '_marginLeft' )\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tchildren: this.content\n\t\t} ).render() as HTMLElement;\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-sticky-panel'\n\t\t\t\t]\n\t\t\t},\n\t\t\tchildren: [\n\t\t\t\tthis._contentPanelPlaceholder,\n\t\t\t\tthis.contentPanelElement\n\t\t\t]\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\t// Check if the panel should go into the sticky state immediately.\n\t\tthis.checkIfShouldBeSticky();\n\n\t\t// Update sticky state of the panel as the window and ancestors are being scrolled.\n\t\tthis.listenTo( global.document, 'scroll', () => {\n\t\t\tthis.checkIfShouldBeSticky();\n\t\t}, { useCapture: true } );\n\n\t\t// Synchronize with `model.isActive` because sticking an inactive panel is pointless.\n\t\tthis.listenTo( this, 'change:isActive', () => {\n\t\t\tthis.checkIfShouldBeSticky();\n\t\t} );\n\t}\n\n\t/**\n\t * Analyzes the environment to decide whether the panel should be sticky or not.\n\t * Then handles the positioning of the panel.\n\t */\n\tpublic checkIfShouldBeSticky(): void {\n\t\t// @if CK_DEBUG_STICKYPANEL // RectDrawer.clear();\n\n\t\tif ( !this.limiterElement || !this.isActive ) {\n\t\t\tthis._unstick();\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst limiterRect = new Rect( this.limiterElement );\n\n\t\tlet visibleLimiterRect = limiterRect.getVisible();\n\n\t\tif ( visibleLimiterRect ) {\n\t\t\tconst windowRect = new Rect( global.window );\n\n\t\t\twindowRect.top += this.viewportTopOffset;\n\t\t\twindowRect.height -= this.viewportTopOffset;\n\n\t\t\tvisibleLimiterRect = visibleLimiterRect.getIntersection( windowRect );\n\t\t}\n\n\t\t// @if CK_DEBUG_STICKYPANEL // if ( visibleLimiterRect ) {\n\t\t// @if CK_DEBUG_STICKYPANEL // \tRectDrawer.draw( visibleLimiterRect,\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'red', outlineOffset: '-3px' },\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t'Visible anc'\n\t\t// @if CK_DEBUG_STICKYPANEL // \t);\n\t\t// @if CK_DEBUG_STICKYPANEL // }\n\t\t// @if CK_DEBUG_STICKYPANEL //\n\t\t// @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( limiterRect,\n\t\t// @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'green', outlineOffset: '-3px' },\n\t\t// @if CK_DEBUG_STICKYPANEL // \t'Limiter'\n\t\t// @if CK_DEBUG_STICKYPANEL // );\n\n\t\t// Stick the panel only if\n\t\t// * the limiter's ancestors are intersecting with each other so that some of their rects are visible,\n\t\t// * and the limiter's top edge is above the visible ancestors' top edge.\n\t\tif ( visibleLimiterRect && limiterRect.top < visibleLimiterRect.top ) {\n\t\t\t// @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( visibleLimiterRect,\n\t\t\t// @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'fuchsia', outlineOffset: '-3px',\n\t\t\t// @if CK_DEBUG_STICKYPANEL // \t\tbackgroundColor: 'rgba(255, 0, 255, .3)' },\n\t\t\t// @if CK_DEBUG_STICKYPANEL // \t'Visible limiter'\n\t\t\t// @if CK_DEBUG_STICKYPANEL // );\n\n\t\t\tconst visibleLimiterTop = visibleLimiterRect.top;\n\n\t\t\t// Check if there's a change the panel can be sticky to the bottom of the limiter.\n\t\t\tif ( visibleLimiterTop + this._contentPanelRect.height + this.limiterBottomOffset > visibleLimiterRect.bottom ) {\n\t\t\t\tconst stickyBottomOffset = Math.max( limiterRect.bottom - visibleLimiterRect.bottom, 0 ) + this.limiterBottomOffset;\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // const stickyBottomOffsetRect = new Rect( {\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // \ttop: limiterRect.bottom - stickyBottomOffset, left: 0, right: 2000,\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // \tbottom: limiterRect.bottom - stickyBottomOffset, width: 2000, height: 1\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // } );\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( stickyBottomOffsetRect,\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '1px', opacity: '.8', outlineColor: 'black' },\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // \t'Sticky bottom offset'\n\t\t\t\t// @if CK_DEBUG_STICKYPANEL // );\n\n\t\t\t\t// Check if sticking the panel to the bottom of the limiter does not cause it to suddenly\n\t\t\t\t// move upwards if there's not enough space for it.\n\t\t\t\tif ( limiterRect.bottom - stickyBottomOffset > limiterRect.top + this._contentPanelRect.height ) {\n\t\t\t\t\tthis._stickToBottomOfLimiter( stickyBottomOffset );\n\t\t\t\t} else {\n\t\t\t\t\tthis._unstick();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ( this._contentPanelRect.height + this.limiterBottomOffset < limiterRect.height ) {\n\t\t\t\t\tthis._stickToTopOfAncestors( visibleLimiterTop );\n\t\t\t\t} else {\n\t\t\t\t\tthis._unstick();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tthis._unstick();\n\t\t}\n\n\t\t// @if CK_DEBUG_STICKYPANEL // console.clear();\n\t\t// @if CK_DEBUG_STICKYPANEL // console.log( 'isSticky', this.isSticky );\n\t\t// @if CK_DEBUG_STICKYPANEL // console.log( '_isStickyToTheBottomOfLimiter', this._isStickyToTheBottomOfLimiter );\n\t\t// @if CK_DEBUG_STICKYPANEL // console.log( '_stickyTopOffset', this._stickyTopOffset );\n\t\t// @if CK_DEBUG_STICKYPANEL // console.log( '_stickyBottomOffset', this._stickyBottomOffset );\n\t\t// @if CK_DEBUG_STICKYPANEL // if ( visibleLimiterRect ) {\n\t\t// @if CK_DEBUG_STICKYPANEL // \tRectDrawer.draw( visibleLimiterRect,\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t{ ...diagonalStylesBlack,\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t\toutlineWidth: '3px', opacity: '.8', outlineColor: 'orange', outlineOffset: '-3px',\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t\tbackgroundColor: 'rgba(0, 0, 255, .2)' },\n\t\t// @if CK_DEBUG_STICKYPANEL // \t\t'visibleLimiterRect'\n\t\t// @if CK_DEBUG_STICKYPANEL // \t);\n\t\t// @if CK_DEBUG_STICKYPANEL // }\n\t}\n\n\t/**\n\t * Sticks the panel at the given CSS `top` offset.\n\t *\n\t * @private\n\t * @param topOffset\n\t */\n\tprivate _stickToTopOfAncestors( topOffset: number ) {\n\t\tthis.isSticky = true;\n\t\tthis._isStickyToTheBottomOfLimiter = false;\n\t\tthis._stickyTopOffset = topOffset;\n\t\tthis._stickyBottomOffset = null;\n\t\tthis._marginLeft = toPx( -global.window.scrollX );\n\t}\n\n\t/**\n\t * Sticks the panel at the bottom of the limiter with a given CSS `bottom` offset.\n\t *\n\t * @private\n\t * @param stickyBottomOffset\n\t */\n\tprivate _stickToBottomOfLimiter( stickyBottomOffset: number ) {\n\t\tthis.isSticky = true;\n\t\tthis._isStickyToTheBottomOfLimiter = true;\n\t\tthis._stickyTopOffset = null;\n\t\tthis._stickyBottomOffset = stickyBottomOffset;\n\t\tthis._marginLeft = toPx( -global.window.scrollX );\n\t}\n\n\t/**\n\t * Unsticks the panel putting it back to its original position.\n\t *\n\t * @private\n\t */\n\tprivate _unstick() {\n\t\tthis.isSticky = false;\n\t\tthis._isStickyToTheBottomOfLimiter = false;\n\t\tthis._stickyTopOffset = null;\n\t\tthis._stickyBottomOffset = null;\n\t\tthis._marginLeft = null;\n\t}\n\n\t/**\n\t * Returns the bounding rect of the {@link #contentPanelElement}.\n\t *\n\t * @private\n\t */\n\tprivate get _contentPanelRect(): Rect {\n\t\treturn new Rect( this.contentPanelElement );\n\t}\n}\n","import toString from './toString.js';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g,\n reHasRegExpChar = RegExp(reRegExpChar.source);\n\n/**\n * Escapes the `RegExp` special characters \"^\", \"$\", \"\\\", \".\", \"*\", \"+\",\n * \"?\", \"(\", \")\", \"[\", \"]\", \"{\", \"}\", and \"|\" in `string`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escapeRegExp('[lodash](https://lodash.com/)');\n * // => '\\[lodash\\]\\(https://lodash\\.com/\\)'\n */\nfunction escapeRegExp(string) {\n string = toString(string);\n return (string && reHasRegExpChar.test(string))\n ? string.replace(reRegExpChar, '\\\\$&')\n : string;\n}\n\nexport default escapeRegExp;\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./search.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./autocomplete.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * The base implementation of `_.propertyOf` without support for deep paths.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Function} Returns the new accessor function.\n */\nfunction basePropertyOf(object) {\n return function(key) {\n return object == null ? undefined : object[key];\n };\n}\n\nexport default basePropertyOf;\n","import basePropertyOf from './_basePropertyOf.js';\n\n/** Used to map characters to HTML entities. */\nvar htmlEscapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n};\n\n/**\n * Used by `_.escape` to convert characters to HTML entities.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\nvar escapeHtmlChar = basePropertyOf(htmlEscapes);\n\nexport default escapeHtmlChar;\n","import escapeHtmlChar from './_escapeHtmlChar.js';\nimport toString from './toString.js';\n\n/** Used to match HTML entities and HTML characters. */\nvar reUnescapedHtml = /[&<>\"']/g,\n reHasUnescapedHtml = RegExp(reUnescapedHtml.source);\n\n/**\n * Converts the characters \"&\", \"<\", \">\", '\"', and \"'\" in `string` to their\n * corresponding HTML entities.\n *\n * **Note:** No other characters are escaped. To escape additional\n * characters use a third-party library like [_he_](https://mths.be/he).\n *\n * Though the \">\" character is escaped for symmetry, characters like\n * \">\" and \"/\" don't need escaping in HTML and have no special meaning\n * unless they're part of a tag or unquoted attribute value. See\n * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)\n * (under \"semi-related fun fact\") for more details.\n *\n * When working with HTML you should always\n * [quote attribute values](http://wonko.com/post/html-escaping) to reduce\n * XSS vectors.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escape('fred, barney, & pebbles');\n * // => 'fred, barney, & pebbles'\n */\nfunction escape(string) {\n string = toString(string);\n return (string && reHasUnescapedHtml.test(string))\n ? string.replace(reUnescapedHtml, escapeHtmlChar)\n : string;\n}\n\nexport default escape;\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./highlightedtext.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./spinner.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./blocktoolbar.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./menubarmenubutton.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenubuttonview\n */\n\nimport IconView from '../icon/iconview.js';\nimport ButtonView from '../button/buttonview.js';\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\n\nimport dropdownArrowIcon from '../../theme/icons/dropdown-arrow.svg';\n\nimport '../../theme/components/menubar/menubarmenubutton.css';\n\n/**\n * A menu {@link module:ui/menubar/menubarmenuview~MenuBarMenuView#buttonView} class. Buttons like this one\n * open both top-level bar menus as well as sub-menus.\n */\nexport default class MenuBarMenuButtonView extends ButtonView {\n\t/**\n\t * An icon that displays an arrow to indicate a direction of the menu.\n\t */\n\tpublic readonly arrowView: IconView;\n\n\t/**\n\t * Creates an instance of the menu bar button view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.set( {\n\t\t\twithText: true,\n\t\t\trole: 'menuitem'\n\t\t} );\n\n\t\tthis.arrowView = this._createArrowView();\n\n\t\tthis.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck-menu-bar__menu__button'\n\t\t\t\t],\n\t\t\t\t'aria-haspopup': true,\n\t\t\t\t'aria-expanded': this.bindTemplate.to( 'isOn', value => String( value ) ),\n\t\t\t\t'data-cke-tooltip-disabled': bind.to( 'isOn' )\n\t\t\t},\n\t\t\ton: {\n\t\t\t\t'mouseenter': bind.to( 'mouseenter' )\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tthis.children.add( this.arrowView );\n\t}\n\n\t/**\n\t * Creates the {@link #arrowView} instance.\n\t */\n\tprivate _createArrowView() {\n\t\tconst arrowView = new IconView();\n\n\t\tarrowView.content = dropdownArrowIcon;\n\t\tarrowView.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\tclass: 'ck-menu-bar__menu__button__arrow'\n\t\t\t}\n\t\t} );\n\n\t\treturn arrowView;\n\t}\n}\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./menubarmenulistitem.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenulistitemview\n */\n\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\nimport ListItemView from '../list/listitemview.js';\nimport type MenuBarMenuView from './menubarmenuview.js';\n\nimport '../../theme/components/menubar/menubarmenulistitem.css';\n\n/**\n * A menu bar list item view, a child of {@link module:ui/menubar/menubarmenulistview~MenuBarMenuListView}.\n *\n * Populate this item with a {@link module:ui/menubar/menubarmenulistitembuttonview~MenuBarMenuListItemButtonView} instance\n * or a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instance to create a sub-menu.\n */\nexport default class MenuBarMenuListItemView extends ListItemView {\n\t/**\n\t * Creates an instance of the list item view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale, parentMenuView: MenuBarMenuView ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck-menu-bar__menu__item'\n\t\t\t\t]\n\t\t\t},\n\t\t\ton: {\n\t\t\t\t'mouseenter': bind.to( 'mouseenter' )\n\t\t\t}\n\t\t} );\n\n\t\tthis.delegate( 'mouseenter' ).to( parentMenuView );\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/utils\n */\n\nimport MenuBarMenuListItemView from './menubarmenulistitemview.js';\nimport type MenuBarMenuView from './menubarmenuview.js';\nimport type {\n\tdefault as MenuBarView,\n\tMenuBarConfig,\n\tMenuBarMenuMouseEnterEvent,\n\tMenuBarMenuChangeIsOpenEvent,\n\tMenuBarMenuArrowRightEvent,\n\tMenuBarMenuArrowLeftEvent,\n\tMenuBarMenuDefinition,\n\tMenuBarConfigObject,\n\tMenuBarConfigAddedGroup,\n\tMenuBarConfigAddedMenu,\n\tMenuBarConfigAddedPosition,\n\tNormalizedMenuBarConfigObject\n} from './menubarview.js';\nimport clickOutsideHandler from '../bindings/clickoutsidehandler.js';\nimport type { ButtonExecuteEvent } from '../button/button.js';\nimport type ComponentFactory from '../componentfactory.js';\nimport type { FocusableView } from '../focuscycler.js';\nimport type { Editor } from '@ckeditor/ckeditor5-core';\nimport {\n\tlogWarning,\n\ttype Locale,\n\ttype ObservableChangeEvent,\n\ttype PositioningFunction\n} from '@ckeditor/ckeditor5-utils';\nimport { cloneDeep } from 'lodash-es';\n\nconst NESTED_PANEL_HORIZONTAL_OFFSET = 5;\n\ntype DeepReadonly = Readonly<{\n\t[K in keyof T]:\n\t\tT[K] extends string ? Readonly\n\t\t\t: T[K] extends Array ? Readonly>>\n\t\t\t\t: DeepReadonly;\n}>;\n\n/**\n * Behaviors of the {@link module:ui/menubar/menubarview~MenuBarView} component.\n */\nexport const MenuBarBehaviors = {\n\t/**\n\t * When the bar is already open:\n\t * * Opens the menu when the user hovers over its button.\n\t * * Closes open menu when another menu's button gets hovered.\n\t */\n\ttoggleMenusAndFocusItemsOnHover( menuBarView: MenuBarView ): void {\n\t\tmenuBarView.on( 'menu:mouseenter', evt => {\n\t\t\t// This works only when the menu bar has already been open and the user hover over the menu bar.\n\t\t\tif ( !menuBarView.isOpen ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor ( const menuView of menuBarView.menus ) {\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // const wasOpen = menuView.isOpen;\n\n\t\t\t\tconst pathLeaf = evt.path[ 0 ];\n\t\t\t\tconst isListItemContainingMenu = pathLeaf instanceof MenuBarMenuListItemView && pathLeaf.children.first === menuView;\n\n\t\t\t\tmenuView.isOpen = ( evt.path.includes( menuView ) || isListItemContainingMenu ) && menuView.isEnabled;\n\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // if ( wasOpen !== menuView.isOpen ) {\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] toggleMenusAndFocusItemsOnHover(): Toggle',\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // \tlogMenu( menuView ), 'isOpen', menuView.isOpen\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // );\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // }\n\t\t\t}\n\n\t\t\t( evt.source as FocusableView ).focus();\n\t\t} );\n\t},\n\n\t/**\n\t * Moves between top-level menus using the arrow left and right keys.\n\t *\n\t * If the menubar has already been open, the arrow keys move focus between top-level menu buttons and open them.\n\t * If the menubar is closed, the arrow keys only move focus between top-level menu buttons.\n\t */\n\tfocusCycleMenusOnArrows( menuBarView: MenuBarView ): void {\n\t\tconst isContentRTL = menuBarView.locale!.uiLanguageDirection === 'rtl';\n\n\t\tmenuBarView.on( 'menu:arrowright', evt => {\n\t\t\tcycleTopLevelMenus( evt.source as MenuBarMenuView, isContentRTL ? -1 : 1 );\n\t\t} );\n\n\t\tmenuBarView.on( 'menu:arrowleft', evt => {\n\t\t\tcycleTopLevelMenus( evt.source as MenuBarMenuView, isContentRTL ? 1 : -1 );\n\t\t} );\n\n\t\tfunction cycleTopLevelMenus( currentMenuView: MenuBarMenuView, step: number ) {\n\t\t\tconst currentIndex = menuBarView.children.getIndex( currentMenuView );\n\t\t\tconst isCurrentMenuViewOpen = currentMenuView.isOpen;\n\t\t\tconst menusCount = menuBarView.children.length;\n\t\t\tconst menuViewToOpen = menuBarView.children.get( ( currentIndex + menusCount + step ) % menusCount )!;\n\n\t\t\tcurrentMenuView.isOpen = false;\n\n\t\t\tif ( isCurrentMenuViewOpen ) {\n\t\t\t\tmenuViewToOpen.isOpen = true;\n\t\t\t}\n\n\t\t\tmenuViewToOpen.buttonView.focus();\n\t\t}\n\t},\n\n\t/**\n\t * Closes the entire sub-menu structure when the bar is closed. This prevents sub-menus from being open if the user\n\t * closes the entire bar, and then re-opens some top-level menu.\n\t */\n\tcloseMenusWhenTheBarCloses( menuBarView: MenuBarView ): void {\n\t\tmenuBarView.on>( 'change:isOpen', () => {\n\t\t\tif ( !menuBarView.isOpen ) {\n\t\t\t\tmenuBarView.menus.forEach( menuView => {\n\t\t\t\t\tmenuView.isOpen = false;\n\n\t\t\t\t\t// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] closeMenusWhenTheBarCloses(): Closing', logMenu( menuView ) );\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Handles the following case:\n\t * 1. Hover to open a sub-menu (A). The button has focus.\n\t * 2. Press arrow up/down to move focus to another sub-menu (B) button.\n\t * 3. Press arrow right to open the sub-menu (B).\n\t * 4. The sub-menu (A) should close as it would with `toggleMenusAndFocusItemsOnHover()`.\n\t */\n\tcloseMenuWhenAnotherOnTheSameLevelOpens( menuBarView: MenuBarView ): void {\n\t\tmenuBarView.on( 'menu:change:isOpen', ( evt, name, isOpen ) => {\n\t\t\tif ( isOpen ) {\n\t\t\t\tmenuBarView.menus\n\t\t\t\t\t.filter( menuView => {\n\t\t\t\t\t\treturn ( evt.source as any ).parentMenuView === menuView.parentMenuView &&\n\t\t\t\t\t\t\tevt.source !== menuView &&\n\t\t\t\t\t\t\tmenuView.isOpen;\n\t\t\t\t\t} ).forEach( menuView => {\n\t\t\t\t\t\tmenuView.isOpen = false;\n\n\t\t\t\t\t\t// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] closeMenuWhenAnotherOpens(): Closing', logMenu( menuView ) );\n\t\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Closes the bar when the user clicked outside of it (page body, editor root, etc.).\n\t */\n\tcloseOnClickOutside( menuBarView: MenuBarView ): void {\n\t\tclickOutsideHandler( {\n\t\t\temitter: menuBarView,\n\t\t\tactivator: () => menuBarView.isOpen,\n\t\t\tcallback: () => menuBarView.close(),\n\t\t\tcontextElements: () => menuBarView.children.map( child => child.element! )\n\t\t} );\n\t}\n};\n\n/**\n * Behaviors of the {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} component.\n */\nexport const MenuBarMenuBehaviors = {\n\t/**\n\t * If the button of the menu is focused, pressing the arrow down key should open the panel and focus it.\n\t * This is analogous to the {@link module:ui/dropdown/dropdownview~DropdownView}.\n\t */\n\topenAndFocusPanelOnArrowDownKey( menuView: MenuBarMenuView ): void {\n\t\tmenuView.keystrokes.set( 'arrowdown', ( data, cancel ) => {\n\t\t\tif ( menuView.focusTracker.focusedElement === menuView.buttonView.element ) {\n\t\t\t\tif ( !menuView.isOpen ) {\n\t\t\t\t\tmenuView.isOpen = true;\n\t\t\t\t}\n\n\t\t\t\tmenuView.panelView.focus();\n\t\t\t\tcancel();\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Open the menu on the right arrow key press. This allows for navigating to sub-menus using the keyboard.\n\t */\n\topenOnArrowRightKey( menuView: MenuBarMenuView ): void {\n\t\tconst keystroke = menuView.locale!.uiLanguageDirection === 'rtl' ? 'arrowleft' : 'arrowright';\n\n\t\tmenuView.keystrokes.set( keystroke, ( data, cancel ) => {\n\t\t\tif ( menuView.focusTracker.focusedElement !== menuView.buttonView.element || !menuView.isEnabled ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] openOnArrowRightKey(): Opening', logMenu( menuView ) );\n\n\t\t\tif ( !menuView.isOpen ) {\n\t\t\t\tmenuView.isOpen = true;\n\t\t\t}\n\n\t\t\tmenuView.panelView.focus();\n\t\t\tcancel();\n\t\t} );\n\t},\n\n\t/**\n\t * Opens the menu on its button click. Note that this behavior only opens but never closes the menu (unlike\n\t * {@link module:ui/dropdown/dropdownview~DropdownView}).\n\t */\n\topenOnButtonClick( menuView: MenuBarMenuView ): void {\n\t\tmenuView.buttonView.on( 'execute', () => {\n\t\t\tmenuView.isOpen = true;\n\t\t\tmenuView.panelView.focus();\n\t\t} );\n\t},\n\n\t/**\n\t * Toggles the menu on its button click. This behavior is analogous to {@link module:ui/dropdown/dropdownview~DropdownView}.\n\t */\n\ttoggleOnButtonClick( menuView: MenuBarMenuView ): void {\n\t\tmenuView.buttonView.on( 'execute', () => {\n\t\t\tmenuView.isOpen = !menuView.isOpen;\n\n\t\t\tif ( menuView.isOpen ) {\n\t\t\t\tmenuView.panelView.focus();\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Closes the menu on the right left key press. This allows for navigating to sub-menus using the keyboard.\n\t */\n\tcloseOnArrowLeftKey( menuView: MenuBarMenuView ): void {\n\t\tconst keystroke = menuView.locale!.uiLanguageDirection === 'rtl' ? 'arrowright' : 'arrowleft';\n\n\t\tmenuView.keystrokes.set( keystroke, ( data, cancel ) => {\n\t\t\tif ( menuView.isOpen ) {\n\t\t\t\tmenuView.isOpen = false;\n\t\t\t\tmenuView.focus();\n\t\t\t\tcancel();\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Closes the menu on the esc key press. This allows for navigating to sub-menus using the keyboard.\n\t */\n\tcloseOnEscKey( menuView: MenuBarMenuView ): void {\n\t\tmenuView.keystrokes.set( 'esc', ( data, cancel ) => {\n\t\t\tif ( menuView.isOpen ) {\n\t\t\t\tmenuView.isOpen = false;\n\t\t\t\tmenuView.focus();\n\t\t\t\tcancel();\n\t\t\t}\n\t\t} );\n\t},\n\n\t/**\n\t * Closes the menu when its parent menu also closed. This prevents from orphaned open menus when the parent menu re-opens.\n\t */\n\tcloseOnParentClose( menuView: MenuBarMenuView ): void {\n\t\tmenuView.parentMenuView!.on>( 'change:isOpen', ( evt, name, isOpen ) => {\n\t\t\tif ( !isOpen && evt.source === menuView.parentMenuView ) {\n\t\t\t\t// @if CK_DEBUG_MENU_BAR // console.log( '[BEHAVIOR] closeOnParentClose(): Closing', logMenu( menuView ) );\n\n\t\t\t\tmenuView.isOpen = false;\n\t\t\t}\n\t\t} );\n\t}\n};\n\n// @if CK_DEBUG_MENU_BAR // function logMenu( menuView: MenuBarMenuView ) {\n// @if CK_DEBUG_MENU_BAR //\treturn `\"${ menuView.buttonView.label }\"`;\n// @if CK_DEBUG_MENU_BAR // }\n\n/**\n * Contains every positioning function used by {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} that decides where the\n * {@link module:ui/menubar/menubarmenuview~MenuBarMenuView#panelView} should be placed.\n *\n * Top-level menu positioning functions:\n *\n *\t┌──────┐\n *\t│ │\n *\t├──────┴────────┐\n *\t│ │\n *\t│ │\n *\t│ │\n *\t│ SE │\n *\t└───────────────┘\n *\n *\t ┌──────┐\n *\t │ │\n *\t┌────────┴──────┤\n *\t│ │\n *\t│ │\n *\t│ │\n *\t│ SW │\n *\t└───────────────┘\n *\n *\t┌───────────────┐\n *\t│ NW │\n *\t│ │\n *\t│ │\n *\t│ │\n *\t└────────┬──────┤\n *\t │ │\n *\t └──────┘\n *\n *\t┌───────────────┐\n *\t│ NE │\n *\t│ │\n *\t│ │\n *\t│ │\n *\t├──────┬────────┘\n *\t│ │\n *\t└──────┘\n *\n * Sub-menu positioning functions:\n *\n *\t┌──────┬───────────────┐\n *\t│ │ │\n *\t└──────┤ │\n *\t │ │\n *\t │ ES │\n *\t └───────────────┘\n *\n *\t┌───────────────┬──────┐\n *\t│ │ │\n *\t│ ├──────┘\n *\t│ │\n *\t│ WS │\n *\t└───────────────┘\n *\n *\t ┌───────────────┐\n *\t │ EN │\n *\t │ │\n *\t┌──────┤ │\n *\t│ │ │\n *\t└──────┴───────────────┘\n *\n *\t┌───────────────┐\n *\t│ WN │\n *\t│ │\n *\t│ ├──────┐\n *\t│ │ │\n *\t└───────────────┴──────┘\n */\nexport const MenuBarMenuViewPanelPositioningFunctions: Record = {\n\tsouthEast: buttonRect => {\n\t\treturn {\n\t\t\ttop: buttonRect.bottom,\n\t\t\tleft: buttonRect.left,\n\t\t\tname: 'se'\n\t\t};\n\t},\n\tsouthWest: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.bottom,\n\t\t\tleft: buttonRect.left - panelRect.width + buttonRect.width,\n\t\t\tname: 'sw'\n\t\t};\n\t},\n\tnorthEast: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.top - panelRect.height,\n\t\t\tleft: buttonRect.left,\n\t\t\tname: 'ne'\n\t\t};\n\t},\n\tnorthWest: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.top - panelRect.height,\n\t\t\tleft: buttonRect.left - panelRect.width + buttonRect.width,\n\t\t\tname: 'nw'\n\t\t};\n\t},\n\teastSouth: buttonRect => {\n\t\treturn {\n\t\t\ttop: buttonRect.top,\n\t\t\tleft: buttonRect.right - NESTED_PANEL_HORIZONTAL_OFFSET,\n\t\t\tname: 'es'\n\t\t};\n\t},\n\teastNorth: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.top - panelRect.height,\n\t\t\tleft: buttonRect.right - NESTED_PANEL_HORIZONTAL_OFFSET,\n\t\t\tname: 'en'\n\t\t};\n\t},\n\twestSouth: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.top,\n\t\t\tleft: buttonRect.left - panelRect.width + NESTED_PANEL_HORIZONTAL_OFFSET,\n\t\t\tname: 'ws'\n\t\t};\n\t},\n\twestNorth: ( buttonRect, panelRect ) => {\n\t\treturn {\n\t\t\ttop: buttonRect.top - panelRect.height,\n\t\t\tleft: buttonRect.left - panelRect.width + NESTED_PANEL_HORIZONTAL_OFFSET,\n\t\t\tname: 'wn'\n\t\t};\n\t}\n} as const;\n\n/**\n * The default items {@link module:core/editor/editorconfig~EditorConfig#menuBar configuration} of the\n * {@link module:ui/menubar/menubarview~MenuBarView} component. It contains names of all menu bar components\n * registered in the {@link module:ui/componentfactory~ComponentFactory component factory} (available in the project).\n *\n * **Note**: Menu bar component names provided by core editor features are prefixed with `menuBar:` in order to distinguish\n * them from components referenced by the {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar configuration}, for instance,\n * `'menuBar:bold'` is a menu bar button but `'bold'` is a toolbar button.\n *\n * Below is the preset menu bar structure (the default value of `config.menuBar.items` property):\n *\n * ```ts\n * [\n * \t{\n * \t\tmenuId: 'file',\n * \t\tlabel: 'File',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'export',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:exportPdf',\n * \t\t\t\t\t'menuBar:exportWord'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'import',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:importWord'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'revisionHistory',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:revisionHistory'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'edit',\n * \t\tlabel: 'Edit',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'undo',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:undo',\n * \t\t\t\t\t'menuBar:redo'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'selectAll',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:selectAll'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'findAndReplace',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:findAndReplace'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'view',\n * \t\tlabel: 'View',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'sourceEditing',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:sourceEditing'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'showBlocks',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:showBlocks'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'restrictedEditingException',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:restrictedEditingException'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'insert',\n * \t\tlabel: 'Insert',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'insertMainWidgets',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:uploadImage',\n * \t\t\t\t\t'menuBar:ckbox',\n * \t\t\t\t\t'menuBar:ckfinder',\n * \t\t\t\t\t'menuBar:insertTable'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'insertInline',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:link',\n * \t\t\t\t\t'menuBar:comment'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'insertMinorWidgets',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:insertTemplate',\n * \t\t\t\t\t'menuBar:blockQuote',\n * \t\t\t\t\t'menuBar:codeBlock',\n * \t\t\t\t\t'menuBar:htmlEmbed'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'insertStructureWidgets',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:horizontalLine',\n * \t\t\t\t\t'menuBar:pageBreak',\n * \t\t\t\t\t'menuBar:tableOfContents'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'restrictedEditing',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:restrictedEditing'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'format',\n * \t\tlabel: 'Format',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'textAndFont',\n * \t\t\t\titems: [\n * \t\t\t\t\t{\n * \t\t\t\t\t\tmenuId: 'text',\n * \t\t\t\t\t\tlabel: 'Text',\n * \t\t\t\t\t\tgroups: [\n * \t\t\t\t\t\t\t{\n * \t\t\t\t\t\t\t\tgroupId: 'basicStyles',\n * \t\t\t\t\t\t\t\titems: [\n * \t\t\t\t\t\t\t\t\t'menuBar:bold',\n * \t\t\t\t\t\t\t\t\t'menuBar:italic',\n * \t\t\t\t\t\t\t\t\t'menuBar:underline',\n * \t\t\t\t\t\t\t\t\t'menuBar:strikethrough',\n * \t\t\t\t\t\t\t\t\t'menuBar:superscript',\n * \t\t\t\t\t\t\t\t\t'menuBar:subscript',\n * \t\t\t\t\t\t\t\t\t'menuBar:code'\n * \t\t\t\t\t\t\t\t]\n * \t\t\t\t\t\t\t},\n * \t\t\t\t\t\t\t{\n * \t\t\t\t\t\t\t\tgroupId: 'textPartLanguage',\n * \t\t\t\t\t\t\t\titems: [\n * \t\t\t\t\t\t\t\t\t'menuBar:textPartLanguage'\n * \t\t\t\t\t\t\t\t]\n * \t\t\t\t\t\t\t}\n * \t\t\t\t\t\t]\n * \t\t\t\t\t},\n * \t\t\t\t\t{\n * \t\t\t\t\t\tmenuId: 'font',\n * \t\t\t\t\t\tlabel: 'Font',\n * \t\t\t\t\t\tgroups: [\n * \t\t\t\t\t\t\t{\n * \t\t\t\t\t\t\t\tgroupId: 'fontProperties',\n * \t\t\t\t\t\t\t\titems: [\n * \t\t\t\t\t\t\t\t\t'menuBar:fontSize',\n * \t\t\t\t\t\t\t\t\t'menuBar:fontFamily'\n * \t\t\t\t\t\t\t\t]\n * \t\t\t\t\t\t\t},\n * \t\t\t\t\t\t\t{\n * \t\t\t\t\t\t\t\tgroupId: 'fontColors',\n * \t\t\t\t\t\t\t\titems: [\n * \t\t\t\t\t\t\t\t\t'menuBar:fontColor',\n * \t\t\t\t\t\t\t\t\t'menuBar:fontBackgroundColor'\n * \t\t\t\t\t\t\t\t]\n * \t\t\t\t\t\t\t},\n * \t\t\t\t\t\t\t{\n * \t\t\t\t\t\t\t\tgroupId: 'highlight',\n * \t\t\t\t\t\t\t\titems: [\n * \t\t\t\t\t\t\t\t\t'menuBar:highlight'\n * \t\t\t\t\t\t\t\t]\n * \t\t\t\t\t\t\t}\n * \t\t\t\t\t\t]\n * \t\t\t\t\t},\n * \t\t\t\t\t'menuBar:heading'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'list',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:bulletedList',\n * \t\t\t\t\t'menuBar:numberedList',\n * \t\t\t\t\t'menuBar:todoList'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'indent',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:alignment',\n * \t\t\t\t\t'menuBar:indent',\n * \t\t\t\t\t'menuBar:outdent'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'caseChange',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:caseChange'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'removeFormat',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:removeFormat'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'tools',\n * \t\tlabel: 'Tools',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'aiTools',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:aiAssistant',\n * \t\t\t\t\t'menuBar:aiCommands'\n * \t\t\t\t]\n * \t\t\t},\n * \t\t\t{\n * \t\t\t\tgroupId: 'tools',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:trackChanges',\n * \t\t\t\t\t'menuBar:commentsArchive'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t},\n * \t{\n * \t\tmenuId: 'help',\n * \t\tlabel: 'Help',\n * \t\tgroups: [\n * \t\t\t{\n * \t\t\t\tgroupId: 'help',\n * \t\t\t\titems: [\n * \t\t\t\t\t'menuBar:accessibilityHelp'\n * \t\t\t\t]\n * \t\t\t}\n * \t\t]\n * \t}\n * ];\n * ```\n *\n * The menu bar can be customized using the `config.menuBar.removeItems` and `config.menuBar.addItems` properties.\n */\n// **NOTE: Whenever you make changes to this value, reflect it in the documentation above!**\nexport const DefaultMenuBarItems: DeepReadonly = [\n\t{\n\t\tmenuId: 'file',\n\t\tlabel: 'File',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'export',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:exportPdf',\n\t\t\t\t\t'menuBar:exportWord'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'import',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:importWord'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'revisionHistory',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:revisionHistory'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'edit',\n\t\tlabel: 'Edit',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'undo',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:undo',\n\t\t\t\t\t'menuBar:redo'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'selectAll',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:selectAll'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'findAndReplace',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:findAndReplace'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'view',\n\t\tlabel: 'View',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'sourceEditing',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:sourceEditing'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'showBlocks',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:showBlocks'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'restrictedEditingException',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:restrictedEditingException'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'insert',\n\t\tlabel: 'Insert',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'insertMainWidgets',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:uploadImage',\n\t\t\t\t\t'menuBar:ckbox',\n\t\t\t\t\t'menuBar:ckfinder',\n\t\t\t\t\t'menuBar:insertTable'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'insertInline',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:link',\n\t\t\t\t\t'menuBar:comment'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'insertMinorWidgets',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:insertTemplate',\n\t\t\t\t\t'menuBar:blockQuote',\n\t\t\t\t\t'menuBar:codeBlock',\n\t\t\t\t\t'menuBar:htmlEmbed'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'insertStructureWidgets',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:horizontalLine',\n\t\t\t\t\t'menuBar:pageBreak',\n\t\t\t\t\t'menuBar:tableOfContents'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'restrictedEditing',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:restrictedEditing'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'format',\n\t\tlabel: 'Format',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'textAndFont',\n\t\t\t\titems: [\n\t\t\t\t\t{\n\t\t\t\t\t\tmenuId: 'text',\n\t\t\t\t\t\tlabel: 'Text',\n\t\t\t\t\t\tgroups: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tgroupId: 'basicStyles',\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t'menuBar:bold',\n\t\t\t\t\t\t\t\t\t'menuBar:italic',\n\t\t\t\t\t\t\t\t\t'menuBar:underline',\n\t\t\t\t\t\t\t\t\t'menuBar:strikethrough',\n\t\t\t\t\t\t\t\t\t'menuBar:superscript',\n\t\t\t\t\t\t\t\t\t'menuBar:subscript',\n\t\t\t\t\t\t\t\t\t'menuBar:code'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tgroupId: 'textPartLanguage',\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t'menuBar:textPartLanguage'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tmenuId: 'font',\n\t\t\t\t\t\tlabel: 'Font',\n\t\t\t\t\t\tgroups: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tgroupId: 'fontProperties',\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t'menuBar:fontSize',\n\t\t\t\t\t\t\t\t\t'menuBar:fontFamily'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tgroupId: 'fontColors',\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t'menuBar:fontColor',\n\t\t\t\t\t\t\t\t\t'menuBar:fontBackgroundColor'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tgroupId: 'highlight',\n\t\t\t\t\t\t\t\titems: [\n\t\t\t\t\t\t\t\t\t'menuBar:highlight'\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\t'menuBar:heading'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'list',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:bulletedList',\n\t\t\t\t\t'menuBar:numberedList',\n\t\t\t\t\t'menuBar:todoList'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'indent',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:alignment',\n\t\t\t\t\t'menuBar:indent',\n\t\t\t\t\t'menuBar:outdent'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'caseChange',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:caseChange'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'removeFormat',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:removeFormat'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'tools',\n\t\tlabel: 'Tools',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'aiTools',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:aiAssistant',\n\t\t\t\t\t'menuBar:aiCommands'\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\tgroupId: 'tools',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:trackChanges',\n\t\t\t\t\t'menuBar:commentsArchive'\n\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\tmenuId: 'help',\n\t\tlabel: 'Help',\n\t\tgroups: [\n\t\t\t{\n\t\t\t\tgroupId: 'help',\n\t\t\t\titems: [\n\t\t\t\t\t'menuBar:accessibilityHelp'\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n];\n\n/**\n * Performs a cleanup and normalization of the menu bar configuration.\n */\nexport function normalizeMenuBarConfig( config: Readonly ): NormalizedMenuBarConfigObject {\n\tlet configObject: NormalizedMenuBarConfigObject;\n\n\t// The integrator specified the config as an object but without items. Let's give them defaults but respect their\n\t// additions and removals.\n\tif ( !( 'items' in config ) || !config.items ) {\n\t\tconfigObject = {\n\t\t\titems: cloneDeep( DefaultMenuBarItems ) as Array,\n\t\t\taddItems: [],\n\t\t\tremoveItems: [],\n\t\t\tisVisible: true,\n\t\t\tisUsingDefaultConfig: true,\n\t\t\t...config\n\t\t};\n\t}\n\t// The integrator specified the config as an object and there are items there. Let's take it as it is.\n\telse {\n\t\tconfigObject = {\n\t\t\titems: config.items,\n\t\t\tremoveItems: [],\n\t\t\taddItems: [],\n\t\t\tisVisible: true,\n\t\t\tisUsingDefaultConfig: false,\n\t\t\t...config\n\t\t};\n\t}\n\n\treturn configObject;\n}\n\n/**\n * Processes a normalized menu bar config and returns a config clone with the following modifications:\n *\n * * Removed components that are not available in the component factory,\n * * Removed obsolete separators,\n * * Purged empty menus,\n * * Localized top-level menu labels.\n */\nexport function processMenuBarConfig( {\n\tnormalizedConfig,\n\tlocale,\n\tcomponentFactory\n}: {\n\tnormalizedConfig: NormalizedMenuBarConfigObject;\n\tlocale: Locale;\n\tcomponentFactory: ComponentFactory;\n} ): NormalizedMenuBarConfigObject {\n\tconst configClone = cloneDeep( normalizedConfig ) as NormalizedMenuBarConfigObject;\n\n\thandleRemovals( normalizedConfig, configClone );\n\thandleAdditions( normalizedConfig, configClone );\n\tpurgeUnavailableComponents( normalizedConfig, configClone, componentFactory );\n\tpurgeEmptyMenus( normalizedConfig, configClone );\n\tlocalizeMenuLabels( configClone, locale );\n\n\treturn configClone;\n}\n\n/**\n * Removes items from the menu bar config based on user `removeItems` configuration. Users can remove\n * individual items, groups, or entire menus. For each removed item, a warning is logged if the item\n * was not found in the configuration.\n */\nfunction handleRemovals(\n\toriginalConfig: NormalizedMenuBarConfigObject,\n\tconfig: NormalizedMenuBarConfigObject\n) {\n\tconst itemsToBeRemoved = config.removeItems;\n\tconst successfullyRemovedItems: Array = [];\n\n\t// Remove top-level menus.\n\tconfig.items = config.items.filter( ( { menuId } ) => {\n\t\tif ( itemsToBeRemoved.includes( menuId ) ) {\n\t\t\tsuccessfullyRemovedItems.push( menuId );\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t} );\n\n\twalkConfigMenus( config.items, menuDefinition => {\n\t\t// Remove groups from menus.\n\t\tmenuDefinition.groups = menuDefinition.groups.filter( ( { groupId } ) => {\n\t\t\tif ( itemsToBeRemoved.includes( groupId ) ) {\n\t\t\t\tsuccessfullyRemovedItems.push( groupId );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} );\n\n\t\t// Remove sub-menus and items from groups.\n\t\tfor ( const groupDefinition of menuDefinition.groups ) {\n\t\t\tgroupDefinition.items = groupDefinition.items.filter( item => {\n\t\t\t\tconst itemId = getIdFromGroupItem( item );\n\n\t\t\t\tif ( itemsToBeRemoved.includes( itemId ) ) {\n\t\t\t\t\tsuccessfullyRemovedItems.push( itemId );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} );\n\t\t}\n\t} );\n\n\tfor ( const itemName of itemsToBeRemoved ) {\n\t\tif ( !successfullyRemovedItems.includes( itemName ) ) {\n\t\t\t/**\n\t\t\t * There was a problem processing the configuration of the menu bar. The item with the given\n\t\t\t * name does could not be removed from the menu bar configuration.\n\t\t\t *\n\t\t\t * This warning usually shows up when the {@link module:core/plugin~Plugin} which is supposed\n\t\t\t * to provide a menu bar item has not been loaded or there is a typo in the\n\t\t\t * {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar configuration}.\n\t\t\t *\n\t\t\t * @error menu-bar-item-could-not-be-removed\n\t\t\t * @param menuBarConfig The full configuration of the menu bar.\n\t\t\t * @param itemName The name of the item that was not removed from the menu bar.\n\t\t\t */\n\t\t\tlogWarning( 'menu-bar-item-could-not-be-removed', {\n\t\t\t\tmenuBarConfig: originalConfig,\n\t\t\t\titemName\n\t\t\t} );\n\t\t}\n\t}\n}\n\n/**\n * Handles the `config.menuBar.addItems` configuration. It allows for adding menus, groups, and items at arbitrary\n * positions in the menu bar. If the position does not exist, a warning is logged.\n */\nfunction handleAdditions(\n\toriginalConfig: NormalizedMenuBarConfigObject,\n\tconfig: NormalizedMenuBarConfigObject\n) {\n\tconst itemsToBeAdded = config.addItems;\n\tconst successFullyAddedItems: typeof itemsToBeAdded = [];\n\n\tfor ( const itemToAdd of itemsToBeAdded ) {\n\t\tconst relation = getRelationFromPosition( itemToAdd.position );\n\t\tconst relativeId = getRelativeIdFromPosition( itemToAdd.position );\n\n\t\t// Adding a menu.\n\t\tif ( isMenuBarMenuAddition( itemToAdd ) ) {\n\t\t\tif ( !relativeId ) {\n\t\t\t\t// Adding a top-level menu at the beginning of the menu bar.\n\t\t\t\tif ( relation === 'start' ) {\n\t\t\t\t\tconfig.items.unshift( itemToAdd.menu );\n\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t}\n\t\t\t\t// Adding a top-level menu at the end of the menu bar.\n\t\t\t\telse if ( relation === 'end' ) {\n\t\t\t\t\tconfig.items.push( itemToAdd.menu );\n\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst topLevelMenuDefinitionIndex = config.items.findIndex( menuDefinition => menuDefinition.menuId === relativeId );\n\n\t\t\t\t// Adding a top-level menu somewhere between existing menu bar menus.\n\t\t\t\tif ( topLevelMenuDefinitionIndex != -1 ) {\n\t\t\t\t\tif ( relation === 'before' ) {\n\t\t\t\t\t\tconfig.items.splice( topLevelMenuDefinitionIndex, 0, itemToAdd.menu );\n\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t} else if ( relation === 'after' ) {\n\t\t\t\t\t\tconfig.items.splice( topLevelMenuDefinitionIndex + 1, 0, itemToAdd.menu );\n\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Adding a sub-menu to an existing items group.\n\t\t\t\telse {\n\t\t\t\t\tconst wasAdded = addMenuOrItemToGroup( config, itemToAdd.menu, relativeId, relation );\n\n\t\t\t\t\tif ( wasAdded ) {\n\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Adding a group.\n\t\telse if ( isMenuBarMenuGroupAddition( itemToAdd ) ) {\n\t\t\twalkConfigMenus( config.items, menuDefinition => {\n\t\t\t\tif ( menuDefinition.menuId === relativeId ) {\n\t\t\t\t\t// Add a group at the start of a menu.\n\t\t\t\t\tif ( relation === 'start' ) {\n\t\t\t\t\t\tmenuDefinition.groups.unshift( itemToAdd.group );\n\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t}\n\t\t\t\t\t// Add a group at the end of a menu.\n\t\t\t\t\telse if ( relation === 'end' ) {\n\t\t\t\t\t\tmenuDefinition.groups.push( itemToAdd.group );\n\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst relativeGroupIndex = menuDefinition.groups.findIndex( group => group.groupId === relativeId );\n\n\t\t\t\t\tif ( relativeGroupIndex !== -1 ) {\n\t\t\t\t\t\t// Add a group before an existing group in a menu.\n\t\t\t\t\t\tif ( relation === 'before' ) {\n\t\t\t\t\t\t\tmenuDefinition.groups.splice( relativeGroupIndex, 0, itemToAdd.group );\n\t\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Add a group after an existing group in a menu.\n\t\t\t\t\t\telse if ( relation === 'after' ) {\n\t\t\t\t\t\t\tmenuDefinition.groups.splice( relativeGroupIndex + 1, 0, itemToAdd.group );\n\t\t\t\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t\t// Adding an item to an existing items group.\n\t\telse {\n\t\t\tconst wasAdded = addMenuOrItemToGroup( config, itemToAdd.item, relativeId, relation );\n\n\t\t\tif ( wasAdded ) {\n\t\t\t\tsuccessFullyAddedItems.push( itemToAdd );\n\t\t\t}\n\t\t}\n\t}\n\n\tfor ( const addedItemConfig of itemsToBeAdded ) {\n\t\tif ( !successFullyAddedItems.includes( addedItemConfig ) ) {\n\t\t\t/**\n\t\t\t * There was a problem processing the configuration of the menu bar. The configured item could not be added\n\t\t\t * because the position it was supposed to be added to does not exist.\n\t\t\t *\n\t\t\t * This warning usually shows up when the {@link module:core/plugin~Plugin} which is supposed\n\t\t\t * to provide a menu bar item has not been loaded or there is a typo in the\n\t\t\t * {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar configuration}.\n\t\t\t *\n\t\t\t * @error menu-bar-item-could-not-be-removed\n\t\t\t * @param menuBarConfig The full configuration of the menu bar.\n\t\t\t * @param itemName The name of the item that was not removed from the menu bar.\n\t\t\t */\n\t\t\tlogWarning( 'menu-bar-item-could-not-be-added', {\n\t\t\t\tmenuBarConfig: originalConfig,\n\t\t\t\taddedItemConfig\n\t\t\t} );\n\t\t}\n\t}\n}\n\n/**\n * Handles adding a sub-menu or an item into a group. The logic is the same for both cases.\n */\nfunction addMenuOrItemToGroup(\n\tconfig: NormalizedMenuBarConfigObject,\n\titemOrMenuToAdd: string | MenuBarMenuDefinition,\n\trelativeId: string | null,\n\trelation: 'start' | 'end' | 'before' | 'after'\n): boolean {\n\tlet wasAdded = false;\n\n\twalkConfigMenus( config.items, menuDefinition => {\n\t\tfor ( const { groupId, items: groupItems } of menuDefinition.groups ) {\n\t\t\t// Avoid infinite loops.\n\t\t\tif ( wasAdded ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( groupId === relativeId ) {\n\t\t\t\t// Adding an item/menu at the beginning of a group.\n\t\t\t\tif ( relation === 'start' ) {\n\t\t\t\t\tgroupItems.unshift( itemOrMenuToAdd );\n\t\t\t\t\twasAdded = true;\n\t\t\t\t}\n\t\t\t\t// Adding an item/menu at the end of a group.\n\t\t\t\telse if ( relation === 'end' ) {\n\t\t\t\t\tgroupItems.push( itemOrMenuToAdd );\n\t\t\t\t\twasAdded = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Adding an item/menu relative to an existing item/menu.\n\t\t\t\tconst relativeItemIndex = groupItems.findIndex( groupItem => {\n\t\t\t\t\treturn getIdFromGroupItem( groupItem ) === relativeId;\n\t\t\t\t} );\n\n\t\t\t\tif ( relativeItemIndex !== -1 ) {\n\t\t\t\t\tif ( relation === 'before' ) {\n\t\t\t\t\t\tgroupItems.splice( relativeItemIndex, 0, itemOrMenuToAdd );\n\t\t\t\t\t\twasAdded = true;\n\t\t\t\t\t} else if ( relation === 'after' ) {\n\t\t\t\t\t\tgroupItems.splice( relativeItemIndex + 1, 0, itemOrMenuToAdd );\n\t\t\t\t\t\twasAdded = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} );\n\n\treturn wasAdded;\n}\n\n/**\n * Removes components from the menu bar configuration that are not available in the factory and would\n * not be instantiated. Warns about missing components if the menu bar configuration was specified by the user.\n */\nfunction purgeUnavailableComponents(\n\toriginalConfig: DeepReadonly,\n\tconfig: NormalizedMenuBarConfigObject,\n\tcomponentFactory: ComponentFactory\n) {\n\twalkConfigMenus( config.items, menuDefinition => {\n\t\tfor ( const groupDefinition of menuDefinition.groups ) {\n\t\t\tgroupDefinition.items = groupDefinition.items.filter( item => {\n\t\t\t\tconst isItemUnavailable = typeof item === 'string' && !componentFactory.has( item );\n\n\t\t\t\t// The default configuration contains all possible editor features. But integrators' editors rarely load\n\t\t\t\t// every possible feature. This is why we do not want to log warnings about unavailable items for the default config\n\t\t\t\t// because they would show up in almost every integration. If the configuration has been provided by\n\t\t\t\t// the integrator, on the other hand, then these warnings bring value.\n\t\t\t\tif ( isItemUnavailable && !config.isUsingDefaultConfig ) {\n\t\t\t\t\t/**\n\t\t\t\t\t * There was a problem processing the configuration of the menu bar. The item with the given\n\t\t\t\t\t * name does not exist so it was omitted when rendering the menu bar.\n\t\t\t\t\t *\n\t\t\t\t\t * This warning usually shows up when the {@link module:core/plugin~Plugin} which is supposed\n\t\t\t\t\t * to provide a menu bar item has not been loaded or there is a typo in the\n\t\t\t\t\t * {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar configuration}.\n\t\t\t\t\t *\n\t\t\t\t\t * Make sure the plugin responsible for this menu bar item is loaded and the menu bar configuration\n\t\t\t\t\t * is correct, e.g. {@link module:basic-styles/bold/boldui~BoldUI} is loaded for the `'menuBar:bold'`\n\t\t\t\t\t * menu bar item.\n\t\t\t\t\t *\n\t\t\t\t\t * @error menu-bar-item-unavailable\n\t\t\t\t\t * @param menuBarConfig The full configuration of the menu bar.\n\t\t\t\t\t * @param parentMenuConfig The config of the menu the unavailable component was defined in.\n\t\t\t\t\t * @param componentName The name of the unavailable component.\n\t\t\t\t\t */\n\t\t\t\t\tlogWarning( 'menu-bar-item-unavailable', {\n\t\t\t\t\t\tmenuBarConfig: originalConfig,\n\t\t\t\t\t\tparentMenuConfig: cloneDeep( menuDefinition ),\n\t\t\t\t\t\tcomponentName: item\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\treturn !isItemUnavailable;\n\t\t\t} );\n\t\t}\n\t} );\n}\n\n/**\n * Removes empty menus from the menu bar configuration to improve the visual UX. Such menus can occur\n * when some plugins responsible for providing menu bar items have not been loaded and some part of\n * the configuration populated menus using these components exclusively.\n */\nfunction purgeEmptyMenus(\n\toriginalConfig: NormalizedMenuBarConfigObject,\n\tconfig: NormalizedMenuBarConfigObject\n) {\n\tconst isUsingDefaultConfig = config.isUsingDefaultConfig;\n\tlet wasSubMenuPurged = false;\n\n\t// Purge top-level menus.\n\tconfig.items = config.items.filter( menuDefinition => {\n\t\tif ( !menuDefinition.groups.length ) {\n\t\t\twarnAboutEmptyMenu( originalConfig, menuDefinition, isUsingDefaultConfig );\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t} );\n\n\t// Warn if there were no top-level menus left in the menu bar after purging.\n\tif ( !config.items.length ) {\n\t\twarnAboutEmptyMenu( originalConfig, originalConfig, isUsingDefaultConfig );\n\n\t\treturn;\n\t}\n\n\t// Purge sub-menus and groups.\n\twalkConfigMenus( config.items, menuDefinition => {\n\t\t// Get rid of empty groups.\n\t\tmenuDefinition.groups = menuDefinition.groups.filter( groupDefinition => {\n\t\t\tif ( !groupDefinition.items.length ) {\n\t\t\t\twasSubMenuPurged = true;\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} );\n\n\t\t// Get rid of empty sub-menus.\n\t\tfor ( const groupDefinition of menuDefinition.groups ) {\n\t\t\tgroupDefinition.items = groupDefinition.items.filter( item => {\n\t\t\t\t// If no groups were left after removing empty ones.\n\t\t\t\tif ( isMenuDefinition( item ) && !item.groups.length ) {\n\t\t\t\t\twarnAboutEmptyMenu( originalConfig, item, isUsingDefaultConfig );\n\t\t\t\t\twasSubMenuPurged = true;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} );\n\t\t}\n\t} );\n\n\tif ( wasSubMenuPurged ) {\n\t\t// The config is walked from the root to the leaves so if anything gets removed, we need to re-run the\n\t\t// whole process because it could've affected parents.\n\t\tpurgeEmptyMenus( originalConfig, config );\n\t}\n}\n\nfunction warnAboutEmptyMenu(\n\toriginalConfig: NormalizedMenuBarConfigObject,\n\temptyMenuConfig: MenuBarMenuDefinition | DeepReadonly,\n\tisUsingDefaultConfig: boolean\n) {\n\tif ( isUsingDefaultConfig ) {\n\t\treturn;\n\t}\n\n\t/**\n\t * There was a problem processing the configuration of the menu bar. One of the menus\n\t * is empty so it was omitted when rendering the menu bar.\n\t *\n\t * This warning usually shows up when some {@link module:core/plugin~Plugin plugins} responsible for\n\t * providing menu bar items have not been loaded and the\n\t * {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar configuration} was not updated.\n\t *\n\t * Make sure all necessary editor plugins are loaded and/or update the menu bar configuration\n\t * to account for the missing menu items.\n\t *\n\t * @error menu-bar-menu-empty\n\t * @param menuBarConfig The full configuration of the menu bar.\n\t * @param emptyMenuConfig The definition of the menu that has no child items.\n\t */\n\tlogWarning( 'menu-bar-menu-empty', {\n\t\tmenuBarConfig: originalConfig,\n\t\temptyMenuConfig\n\t} );\n}\n\n/**\n * Localizes the user-config using pre-defined localized category labels.\n */\nfunction localizeMenuLabels( config: NormalizedMenuBarConfigObject, locale: Locale ) {\n\tconst t = locale.t;\n\tconst localizedCategoryLabels: Record = {\n\t\t// Top-level categories.\n\t\t'File': t( {\n\t\t\tstring: 'File',\n\t\t\tid: 'MENU_BAR_MENU_FILE'\n\t\t} ),\n\t\t'Edit': t( {\n\t\t\tstring: 'Edit',\n\t\t\tid: 'MENU_BAR_MENU_EDIT'\n\t\t} ),\n\t\t'View': t( {\n\t\t\tstring: 'View',\n\t\t\tid: 'MENU_BAR_MENU_VIEW'\n\t\t} ),\n\t\t'Insert': t( {\n\t\t\tstring: 'Insert',\n\t\t\tid: 'MENU_BAR_MENU_INSERT'\n\t\t} ),\n\t\t'Format': t( {\n\t\t\tstring: 'Format',\n\t\t\tid: 'MENU_BAR_MENU_FORMAT'\n\t\t} ),\n\t\t'Tools': t( {\n\t\t\tstring: 'Tools',\n\t\t\tid: 'MENU_BAR_MENU_TOOLS'\n\t\t} ),\n\t\t'Help': t( {\n\t\t\tstring: 'Help',\n\t\t\tid: 'MENU_BAR_MENU_HELP'\n\t\t} ),\n\n\t\t// Sub-menus.\n\t\t'Text': t( {\n\t\t\tstring: 'Text',\n\t\t\tid: 'MENU_BAR_MENU_TEXT'\n\t\t} ),\n\t\t'Font': t( {\n\t\t\tstring: 'Font',\n\t\t\tid: 'MENU_BAR_MENU_FONT'\n\t\t} )\n\t};\n\n\twalkConfigMenus( config.items, definition => {\n\t\tif ( definition.label in localizedCategoryLabels ) {\n\t\t\tdefinition.label = localizedCategoryLabels[ definition.label ];\n\t\t}\n\t} );\n}\n\n/**\n * Recursively visits all menu definitions in the config and calls the callback for each of them.\n */\nfunction walkConfigMenus(\n\tdefinition: NormalizedMenuBarConfigObject[ 'items' ] | MenuBarMenuDefinition,\n\tcallback: ( definition: MenuBarMenuDefinition ) => void\n) {\n\tif ( Array.isArray( definition ) ) {\n\t\tfor ( const topLevelMenuDefinition of definition ) {\n\t\t\twalk( topLevelMenuDefinition );\n\t\t}\n\t}\n\n\tfunction walk( menuDefinition: MenuBarMenuDefinition ) {\n\t\tcallback( menuDefinition );\n\n\t\tfor ( const groupDefinition of menuDefinition.groups ) {\n\t\t\tfor ( const groupItem of groupDefinition.items ) {\n\t\t\t\tif ( isMenuDefinition( groupItem ) ) {\n\t\t\t\t\twalk( groupItem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction isMenuBarMenuAddition( definition: any ): definition is MenuBarConfigAddedMenu {\n\treturn typeof definition === 'object' && 'menu' in definition;\n}\n\nfunction isMenuBarMenuGroupAddition( definition: any ): definition is MenuBarConfigAddedGroup {\n\treturn typeof definition === 'object' && 'group' in definition;\n}\n\nfunction getRelationFromPosition( position: MenuBarConfigAddedPosition ): 'start' | 'end' | 'before' | 'after' {\n\tif ( position.startsWith( 'start' ) ) {\n\t\treturn 'start';\n\t} else if ( position.startsWith( 'end' ) ) {\n\t\treturn 'end';\n\t} else if ( position.startsWith( 'after' ) ) {\n\t\treturn 'after';\n\t} else {\n\t\treturn 'before';\n\t}\n}\n\nfunction getRelativeIdFromPosition( position: MenuBarConfigAddedPosition ): string | null {\n\tconst match = position.match( /^[^:]+:(.+)/ );\n\n\tif ( match ) {\n\t\treturn match[ 1 ];\n\t}\n\n\treturn null;\n}\n\nfunction getIdFromGroupItem( item: string | MenuBarMenuDefinition ): string {\n\treturn typeof item === 'string' ? item : item.menuId;\n}\n\nfunction isMenuDefinition( definition: any ): definition is MenuBarMenuDefinition {\n\treturn typeof definition === 'object' && 'menuId' in definition;\n}\n\n/**\n * Initializes menu bar for given editor.\n *\n * @internal\n */\nexport function _initMenuBar( editor: Editor, menuBarView: MenuBarView ): void {\n\tconst menuBarViewElement = menuBarView.element!;\n\n\teditor.ui.focusTracker.add( menuBarViewElement );\n\teditor.keystrokes.listenTo( menuBarViewElement );\n\n\tconst normalizedMenuBarConfig = normalizeMenuBarConfig( editor.config.get( 'menuBar' ) || {} );\n\n\tmenuBarView.fillFromConfig( normalizedMenuBarConfig, editor.ui.componentFactory );\n\n\teditor.keystrokes.set( 'Esc', ( data, cancel ) => {\n\t\tif ( menuBarViewElement.contains( editor.ui.focusTracker.focusedElement ) ) {\n\t\t\teditor.editing.view.focus();\n\t\t\tcancel();\n\t\t}\n\t} );\n\n\teditor.keystrokes.set( 'Alt+F9', ( data, cancel ) => {\n\t\tif ( !menuBarViewElement.contains( editor.ui.focusTracker.focusedElement ) ) {\n\t\t\tmenuBarView!.focus();\n\t\t\tcancel();\n\t\t}\n\t} );\n}\n\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./menubarmenupanel.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenupanelview\n */\n\nimport { type Locale } from '@ckeditor/ckeditor5-utils';\nimport type { FocusableView } from '../focuscycler.js';\nimport type ViewCollection from '../viewcollection.js';\nimport View from '../view.js';\n\nimport '../../theme/components/menubar/menubarmenupanel.css';\n\n/**\n * A view representing a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView#panelView} of a menu.\n */\nexport default class MenuBarMenuPanelView extends View implements FocusableView {\n\t/**\n\t * Collection of the child views in this panel.\n\t */\n\tpublic readonly children: ViewCollection;\n\n\t/**\n\t * Controls whether the panel is visible.\n\t *\n\t * @observable\n\t */\n\tdeclare public isVisible: boolean;\n\n\t/**\n\t * The name of the position of the panel, relative to the parent.\n\t *\n\t * This property is reflected in the CSS class suffix set to {@link #element} that controls\n\t * the position of the panel.\n\t *\n\t * @observable\n\t * @default 'se'\n\t */\n\tdeclare public position: MenuBarMenuPanelPosition;\n\n\t/**\n\t * Creates an instance of the menu panel view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale?: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.set( 'isVisible', false );\n\t\tthis.set( 'position', 'se' );\n\n\t\tthis.children = this.createCollection();\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-reset',\n\t\t\t\t\t'ck-menu-bar__menu__panel',\n\t\t\t\t\tbind.to( 'position', value => `ck-menu-bar__menu__panel_position_${ value }` ),\n\t\t\t\t\tbind.if( 'isVisible', 'ck-hidden', value => !value )\n\t\t\t\t],\n\t\t\t\ttabindex: '-1'\n\t\t\t},\n\n\t\t\tchildren: this.children,\n\n\t\t\ton: {\n\t\t\t\t// Drag and drop in the panel should not break the selection in the editor.\n\t\t\t\t// https://github.com/ckeditor/ckeditor5-ui/issues/228\n\t\t\t\tselectstart: bind.to( evt => {\n\t\t\t\t\tif ( ( evt.target as HTMLElement ).tagName.toLocaleLowerCase() === 'input' ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tevt.preventDefault();\n\t\t\t\t} )\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * Focuses the first child of the panel (default) or the last one if the `direction` is `-1`.\n\t */\n\tpublic focus( direction: -1 | 1 = 1 ): void {\n\t\tif ( this.children.length ) {\n\t\t\tif ( direction === 1 ) {\n\t\t\t\tthis.children.first!.focus();\n\t\t\t} else {\n\t\t\t\tthis.children.last!.focus();\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * The names of the positions of the {@link module:ui/menubar/menubarmenupanelview~MenuBarMenuPanelView}.\n *\n * They are reflected as CSS class suffixes on the panel view element.\n */\nexport type MenuBarMenuPanelPosition = 'se' | 'sw' | 'ne' | 'nw' | 'w' | 'e';\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./menubarmenu.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenuview\n */\n\nimport {\n\tFocusTracker,\n\tKeystrokeHandler,\n\tgetOptimalPosition,\n\ttype Locale,\n\ttype PositioningFunction,\n\ttype ObservableChangeEvent\n} from '@ckeditor/ckeditor5-utils';\nimport MenuBarMenuButtonView from './menubarmenubuttonview.js';\nimport { MenuBarMenuBehaviors, MenuBarMenuViewPanelPositioningFunctions } from './utils.js';\nimport type { FocusableView } from '../focuscycler.js';\nimport View from '../view.js';\nimport {\n\tdefault as MenuBarMenuPanelView,\n\ttype MenuBarMenuPanelPosition\n} from './menubarmenupanelview.js';\n\nimport '../../theme/components/menubar/menubarmenu.css';\n\n/**\n * A menu view for the {@link module:ui/menubar/menubarview~MenuBarView}. Menus are building blocks of the menu bar,\n * they host other sub-menus and menu items (buttons) that users can interact with.\n */\nexport default class MenuBarMenuView extends View implements FocusableView {\n\t/**\n\t * Button of the menu view.\n\t */\n\tpublic readonly buttonView: MenuBarMenuButtonView;\n\n\t/**\n\t * Panel of the menu. It hosts children of the menu.\n\t */\n\tpublic readonly panelView: MenuBarMenuPanelView;\n\n\t/**\n\t * Tracks information about the DOM focus in the menu.\n\t */\n\tpublic readonly focusTracker: FocusTracker;\n\n\t/**\n\t * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages\n\t * keystrokes of the menu.\n\t */\n\tpublic readonly keystrokes: KeystrokeHandler;\n\n\t/**\n\t * Controls whether the menu is open, i.e. shows or hides the {@link #panelView panel}.\n\t *\n\t * @observable\n\t */\n\tdeclare public isOpen: boolean;\n\n\t/**\n\t * Controls whether the menu is enabled, i.e. its {@link #buttonView} can be clicked.\n\t *\n\t * @observable\n\t */\n\tdeclare public isEnabled: boolean;\n\n\t/**\n\t * (Optional) The additional CSS class set on the menu {@link #element}.\n\t *\n\t * @observable\n\t */\n\tdeclare public class: string | undefined;\n\n\t/**\n\t * The name of the position of the {@link #panelView}, relative to the menu.\n\t *\n\t * **Note**: The value is updated each time the panel gets {@link #isOpen open}.\n\t *\n\t * @observable\n\t * @default 'w'\n\t */\n\tdeclare public panelPosition: MenuBarMenuPanelPosition;\n\n\t/**\n\t * The parent menu view of the menu. It is `null` for top-level menus.\n\t *\n\t * See {@link module:ui/menubar/menubarview~MenuBarView#registerMenu}.\n\t */\n\tdeclare public parentMenuView: MenuBarMenuView | null;\n\n\t/**\n\t * Creates an instance of the menu view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst bind = this.bindTemplate;\n\n\t\tthis.buttonView = new MenuBarMenuButtonView( locale );\n\t\tthis.buttonView.delegate( 'mouseenter' ).to( this );\n\t\tthis.buttonView.bind( 'isOn', 'isEnabled' ).to( this, 'isOpen', 'isEnabled' );\n\n\t\tthis.panelView = new MenuBarMenuPanelView( locale );\n\t\tthis.panelView.bind( 'isVisible' ).to( this, 'isOpen' );\n\n\t\tthis.keystrokes = new KeystrokeHandler();\n\t\tthis.focusTracker = new FocusTracker();\n\n\t\tthis.set( 'isOpen', false );\n\t\tthis.set( 'isEnabled', true );\n\t\tthis.set( 'panelPosition', 'w' );\n\t\tthis.set( 'class', undefined );\n\t\tthis.set( 'parentMenuView', null );\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-menu-bar__menu',\n\t\t\t\t\tbind.to( 'class' ),\n\t\t\t\t\tbind.if( 'isEnabled', 'ck-disabled', value => !value ),\n\t\t\t\t\tbind.if( 'parentMenuView', 'ck-menu-bar__menu_top-level', value => !value )\n\t\t\t\t]\n\t\t\t},\n\n\t\t\tchildren: [\n\t\t\t\tthis.buttonView,\n\t\t\t\tthis.panelView\n\t\t\t]\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tthis.focusTracker.add( this.buttonView.element! );\n\t\tthis.focusTracker.add( this.panelView.element! );\n\n\t\t// Listen for keystrokes coming from within #element.\n\t\tthis.keystrokes.listenTo( this.element! );\n\n\t\tMenuBarMenuBehaviors.closeOnEscKey( this );\n\n\t\tthis._repositionPanelOnOpen();\n\t}\n\n\t// For now, this method cannot be called in the render process because the `parentMenuView` may be assigned\n\t// after the rendering process.\n\t//\n\t// TODO: We should reconsider the way we handle this logic.\n\t/**\n\t * Attach all keyboard behaviors for the menu bar view.\n\t *\n\t * @internal\n\t */\n\tpublic _attachBehaviors(): void {\n\t\t// Top-level menus.\n\t\tif ( !this.parentMenuView ) {\n\t\t\tthis._propagateArrowKeystrokeEvents();\n\n\t\t\tMenuBarMenuBehaviors.openAndFocusPanelOnArrowDownKey( this );\n\t\t\tMenuBarMenuBehaviors.toggleOnButtonClick( this );\n\t\t} else {\n\t\t\tMenuBarMenuBehaviors.openOnButtonClick( this );\n\t\t\tMenuBarMenuBehaviors.openOnArrowRightKey( this );\n\t\t\tMenuBarMenuBehaviors.closeOnArrowLeftKey( this );\n\t\t\tMenuBarMenuBehaviors.closeOnParentClose( this );\n\t\t}\n\t}\n\n\t/**\n\t * Fires `arrowright` and `arrowleft` events when the user pressed corresponding arrow keys.\n\t */\n\tprivate _propagateArrowKeystrokeEvents(): void {\n\t\tthis.keystrokes.set( 'arrowright', ( data, cancel ) => {\n\t\t\tthis.fire( 'arrowright' );\n\t\t\tcancel();\n\t\t} );\n\n\t\tthis.keystrokes.set( 'arrowleft', ( data, cancel ) => {\n\t\t\tthis.fire( 'arrowleft' );\n\t\t\tcancel();\n\t\t} );\n\t}\n\n\t/**\n\t * Sets the position of the panel when the menu opens. The panel is positioned\n\t * so that it optimally uses the available space in the viewport.\n\t */\n\tprivate _repositionPanelOnOpen(): void {\n\t\t// Let the menu control the position of the panel. The position must be updated every time the menu is open.\n\t\tthis.on>( 'change:isOpen', ( evt, name, isOpen ) => {\n\t\t\tif ( !isOpen ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst optimalPanelPosition = MenuBarMenuView._getOptimalPosition( {\n\t\t\t\telement: this.panelView.element!,\n\t\t\t\ttarget: this.buttonView.element!,\n\t\t\t\tfitInViewport: true,\n\t\t\t\tpositions: this._panelPositions\n\t\t\t} );\n\n\t\t\tthis.panelView.position = (\n\t\t\t\toptimalPanelPosition ? optimalPanelPosition.name : this._panelPositions[ 0 ].name\n\t\t\t) as MenuBarMenuPanelPosition;\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic focus(): void {\n\t\tthis.buttonView.focus();\n\t}\n\n\t/**\n\t * Positioning functions for the {@link #panelView} . They change depending on the role of the menu (top-level vs sub-menu) in\n\t * the {@link module:ui/menubar/menubarview~MenuBarView menu bar} and the UI language direction.\n\t */\n\tpublic get _panelPositions(): Array {\n\t\tconst {\n\t\t\tsouthEast, southWest, northEast, northWest,\n\t\t\twestSouth, eastSouth, westNorth, eastNorth\n\t\t} = MenuBarMenuViewPanelPositioningFunctions;\n\n\t\tif ( this.locale!.uiLanguageDirection === 'ltr' ) {\n\t\t\tif ( this.parentMenuView ) {\n\t\t\t\treturn [ eastSouth, eastNorth, westSouth, westNorth ];\n\t\t\t} else {\n\t\t\t\treturn [ southEast, southWest, northEast, northWest ];\n\t\t\t}\n\t\t} else {\n\t\t\tif ( this.parentMenuView ) {\n\t\t\t\treturn [ westSouth, westNorth, eastSouth, eastNorth ];\n\t\t\t} else {\n\t\t\t\treturn [ southWest, southEast, northWest, northEast ];\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * A function used to calculate the optimal position for the dropdown panel.\n\t *\n\t * Referenced for unit testing purposes.\n\t */\n\tprivate static _getOptimalPosition = getOptimalPosition;\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenulistview\n */\n\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\nimport ListView from '../list/listview.js';\n\n/**\n * A list of menu bar items, a child of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView#panelView}.\n *\n * Use this class to create a list of items (options, buttons) to be displayed in a menu bar.\n *\n * To populate this list, use {@link module:ui/menubar/menubarmenulistitemview~MenuBarMenuListItemView} instances.\n */\nexport default class MenuBarMenuListView extends ListView {\n\t/**\n\t * Creates an instance of the list view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tthis.role = 'menu';\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarmenulistitemfiledialogbuttonview\n */\n\nimport type { Locale } from '@ckeditor/ckeditor5-utils';\nimport FileDialogButtonView from '../button/filedialogbuttonview.js';\n\nimport '../../theme/components/menubar/menubarmenulistitembutton.css';\n\n/**\n * A menu bar list file dialog button view. Buttons like this one execute user actions.\n *\n * This component provides a button that opens the native file selection dialog.\n */\nexport default class MenuBarMenuListItemFileDialogButtonView extends FileDialogButtonView {\n\t/**\n\t * Creates an instance of the menu bar list button view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tthis.set( {\n\t\t\twithText: true,\n\t\t\twithKeystroke: true,\n\t\t\ttooltip: false,\n\t\t\trole: 'menuitem'\n\t\t} );\n\n\t\tthis.extendTemplate( {\n\t\t\tattributes: {\n\t\t\t\tclass: [ 'ck-menu-bar__menu__item__button' ]\n\t\t\t}\n\t\t} );\n\t}\n}\n","import api from \"!../../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../node_modules/css-loader/dist/cjs.js!../../../../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./menubar.css\";\n\nvar options = {\"injectType\":\"singletonStyleTag\",\"attributes\":{\"data-cke\":true}};\n\noptions.insert = \"head\";\noptions.singleton = true;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","/**\n * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module ui/menubar/menubarview\n */\n\nimport {\n\tlogWarning,\n\ttype BaseEvent,\n\ttype Locale,\n\ttype ObservableChangeEvent\n} from '@ckeditor/ckeditor5-utils';\nimport { type FocusableView } from '../focuscycler.js';\nimport View from '../view.js';\nimport { isObject } from 'lodash-es';\nimport ListItemView from '../list/listitemview.js';\nimport ListSeparatorView from '../list/listseparatorview.js';\nimport type ViewCollection from '../viewcollection.js';\nimport type ComponentFactory from '../componentfactory.js';\n\nimport MenuBarMenuView from './menubarmenuview.js';\nimport MenuBarMenuListView from './menubarmenulistview.js';\nimport MenuBarMenuListItemView from './menubarmenulistitemview.js';\nimport MenuBarMenuListItemButtonView from './menubarmenulistitembuttonview.js';\nimport MenuBarMenuListItemFileDialogButtonView from './menubarmenulistitemfiledialogbuttonview.js';\nimport {\n\tMenuBarBehaviors,\n\tprocessMenuBarConfig\n} from './utils.js';\n\nconst EVENT_NAME_DELEGATES = [ 'mouseenter', 'arrowleft', 'arrowright', 'change:isOpen' ] as const;\n\nimport '../../theme/components/menubar/menubar.css';\n\n/**\n * The application menu bar component. It brings a set of top-level menus (and sub-menus) that can be used\n * to organize and access a large number of buttons.\n */\nexport default class MenuBarView extends View implements FocusableView {\n\t/**\n\t * Collection of the child views inside the {@link #element}.\n\t */\n\tpublic children: ViewCollection;\n\n\t/**\n\t * Indicates whether any of top-level menus are open in the menu bar. To close\n\t * the menu bar use the {@link #close} method.\n\t *\n\t * @observable\n\t */\n\tdeclare public isOpen: boolean;\n\n\t/**\n\t * A list of {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instances registered in the menu bar.\n\t *\n\t * @observable\n\t */\n\tpublic menus: Array = [];\n\n\t/**\n\t * Creates an instance of the menu bar view.\n\t *\n\t * @param locale The localization services instance.\n\t */\n\tconstructor( locale: Locale ) {\n\t\tsuper( locale );\n\n\t\tconst t = locale.t;\n\n\t\tthis.set( 'isOpen', false );\n\t\tthis._setupIsOpenUpdater();\n\n\t\tthis.children = this.createCollection();\n\n\t\t// @if CK_DEBUG_MENU_BAR // // Logs events in the main event bus of the component.\n\t\t// @if CK_DEBUG_MENU_BAR // this.on( 'menu', ( evt, data ) => {\n\t\t// @if CK_DEBUG_MENU_BAR // \tconsole.log( `MenuBarView:${ evt.name }`, evt.path.map( view => view.element ) );\n\t\t// @if CK_DEBUG_MENU_BAR // } );\n\n\t\tthis.setTemplate( {\n\t\t\ttag: 'div',\n\t\t\tattributes: {\n\t\t\t\tclass: [\n\t\t\t\t\t'ck',\n\t\t\t\t\t'ck-menu-bar'\n\t\t\t\t],\n\t\t\t\t'aria-label': t( 'Editor menu bar' ),\n\t\t\t\trole: 'menubar'\n\t\t\t},\n\t\t\tchildren: this.children\n\t\t} );\n\t}\n\n\t/**\n\t * A utility that expands a plain menu bar configuration into a structure of menus (also: sub-menus)\n\t * and items using a given {@link module:ui/componentfactory~ComponentFactory component factory}.\n\t *\n\t * See the {@link module:core/editor/editorconfig~EditorConfig#menuBar menu bar} in the editor\n\t * configuration reference to learn how to configure the menu bar.\n\t */\n\tpublic fillFromConfig( config: NormalizedMenuBarConfigObject, componentFactory: ComponentFactory ): void {\n\t\tconst locale = this.locale!;\n\t\tconst processedConfig = processMenuBarConfig( {\n\t\t\tnormalizedConfig: config,\n\t\t\tlocale,\n\t\t\tcomponentFactory\n\t\t} );\n\n\t\tconst topLevelCategoryMenuViews = processedConfig.items.map( menuDefinition => this._createMenu( {\n\t\t\tcomponentFactory,\n\t\t\tmenuDefinition\n\t\t} ) );\n\n\t\tthis.children.addMany( topLevelCategoryMenuViews );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tpublic override render(): void {\n\t\tsuper.render();\n\n\t\tMenuBarBehaviors.toggleMenusAndFocusItemsOnHover( this );\n\t\tMenuBarBehaviors.closeMenusWhenTheBarCloses( this );\n\t\tMenuBarBehaviors.closeMenuWhenAnotherOnTheSameLevelOpens( this );\n\t\tMenuBarBehaviors.focusCycleMenusOnArrows( this );\n\t\tMenuBarBehaviors.closeOnClickOutside( this );\n\t}\n\n\t/**\n\t * Focuses the menu bar.\n\t */\n\tpublic focus(): void {\n\t\tif ( this.children.first ) {\n\t\t\tthis.children.first.focus();\n\t\t}\n\t}\n\n\t/**\n\t * Closes all menus in the bar.\n\t */\n\tpublic close(): void {\n\t\tfor ( const topLevelCategoryMenuView of this.children ) {\n\t\t\ttopLevelCategoryMenuView.isOpen = false;\n\t\t}\n\t}\n\n\t/**\n\t * Registers a menu view in the menu bar. Every {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} instance must be registered\n\t * in the menu bar to be properly managed.\n\t */\n\tpublic registerMenu( menuView: MenuBarMenuView, parentMenuView: MenuBarMenuView | null = null ): void {\n\t\tif ( parentMenuView ) {\n\t\t\tmenuView.delegate( ...EVENT_NAME_DELEGATES ).to( parentMenuView );\n\t\t\tmenuView.parentMenuView = parentMenuView;\n\t\t} else {\n\t\t\tmenuView.delegate( ...EVENT_NAME_DELEGATES ).to( this, name => 'menu:' + name );\n\t\t}\n\n\t\tmenuView._attachBehaviors();\n\n\t\tthis.menus.push( menuView );\n\t}\n\n\t/**\n\t * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} based on the given definition.\n\t */\n\tprivate _createMenu( { componentFactory, menuDefinition, parentMenuView }: {\n\t\tcomponentFactory: ComponentFactory;\n\t\tmenuDefinition: MenuBarMenuDefinition;\n\t\tparentMenuView?: MenuBarMenuView;\n\t} ) {\n\t\tconst locale = this.locale!;\n\t\tconst menuView = new MenuBarMenuView( locale );\n\n\t\tthis.registerMenu( menuView, parentMenuView );\n\n\t\tmenuView.buttonView.set( {\n\t\t\tlabel: menuDefinition.label\n\t\t} );\n\n\t\t// Defer the creation of the menu structure until it gets open. This is a performance optimization\n\t\t// that shortens the time needed to create the editor.\n\t\tmenuView.once>( 'change:isOpen', () => {\n\t\t\tconst listView = new MenuBarMenuListView( locale );\n\t\t\tlistView.ariaLabel = menuDefinition.label;\n\t\t\tmenuView.panelView.children.add( listView );\n\n\t\t\tlistView.items.addMany( this._createMenuItems( { menuDefinition, parentMenuView: menuView, componentFactory } ) );\n\t\t} );\n\n\t\treturn menuView;\n\t}\n\n\t/**\n\t * Creates a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} items based on the given definition.\n\t */\n\tprivate _createMenuItems( { menuDefinition, parentMenuView, componentFactory }: {\n\t\tmenuDefinition: MenuBarMenuDefinition;\n\t\tcomponentFactory: ComponentFactory;\n\t\tparentMenuView: MenuBarMenuView;\n\t} ): Array {\n\t\tconst locale = this.locale!;\n\t\tconst items = [];\n\n\t\tfor ( const menuGroupDefinition of menuDefinition.groups ) {\n\t\t\tfor ( const itemDefinition of menuGroupDefinition.items ) {\n\t\t\t\tconst menuItemView = new MenuBarMenuListItemView( locale, parentMenuView );\n\n\t\t\t\tif ( isObject( itemDefinition ) ) {\n\t\t\t\t\tmenuItemView.children.add( this._createMenu( {\n\t\t\t\t\t\tcomponentFactory,\n\t\t\t\t\t\tmenuDefinition: itemDefinition,\n\t\t\t\t\t\tparentMenuView\n\t\t\t\t\t} ) );\n\t\t\t\t} else {\n\t\t\t\t\tconst componentView = this._createMenuItemContentFromFactory( {\n\t\t\t\t\t\tcomponentName: itemDefinition,\n\t\t\t\t\t\tcomponentFactory,\n\t\t\t\t\t\tparentMenuView\n\t\t\t\t\t} );\n\n\t\t\t\t\tif ( !componentView ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tmenuItemView.children.add( componentView );\n\t\t\t\t}\n\n\t\t\t\titems.push( menuItemView );\n\t\t\t}\n\n\t\t\t// Separate groups with a separator.\n\t\t\tif ( menuGroupDefinition !== menuDefinition.groups[ menuDefinition.groups.length - 1 ] ) {\n\t\t\t\titems.push( new ListSeparatorView( locale ) );\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t/**\n\t * Uses the component factory to create a content of the menu item (a button or a sub-menu).\n\t */\n\tprivate _createMenuItemContentFromFactory( { componentName, parentMenuView, componentFactory }: {\n\t\tcomponentName: string;\n\t\tcomponentFactory: ComponentFactory;\n\t\tparentMenuView: MenuBarMenuView;\n\t} ): MenuBarMenuView | MenuBarMenuListItemButtonView | null {\n\t\tconst componentView = componentFactory.create( componentName );\n\n\t\tif ( !(\n\t\t\tcomponentView instanceof MenuBarMenuView ||\n\t\t\tcomponentView instanceof MenuBarMenuListItemButtonView ||\n\t\t\tcomponentView instanceof MenuBarMenuListItemFileDialogButtonView\n\t\t) ) {\n\t\t\t/**\n\t\t\t * Adding unsupported components to the {@link module:ui/menubar/menubarview~MenuBarView} is not possible.\n\t\t\t *\n\t\t\t * A component should be either a {@link module:ui/menubar/menubarmenuview~MenuBarMenuView} (sub-menu) or a\n\t\t\t * {@link module:ui/menubar/menubarmenulistitembuttonview~MenuBarMenuListItemButtonView} (button).\n\t\t\t *\n\t\t\t * @error menu-bar-component-unsupported\n\t\t\t * @param componentName A name of the unsupported component used in the configuration.\n\t\t\t * @param componentView An unsupported component view.\n\t\t\t */\n\t\t\tlogWarning( 'menu-bar-component-unsupported', {\n\t\t\t\tcomponentName,\n\t\t\t\tcomponentView\n\t\t\t} );\n\n\t\t\treturn null;\n\t\t}\n\n\t\tthis._registerMenuTree( componentView, parentMenuView );\n\n\t\t// Close the whole menu bar when a component is executed.\n\t\tcomponentView.on( 'execute', () => {\n\t\t\tthis.close();\n\t\t} );\n\n\t\treturn componentView;\n\t}\n\n\t/**\n\t * Checks component and its children recursively and calls {@link #registerMenu}\n\t * for each item that is {@link module:ui/menubar/menubarmenuview~MenuBarMenuView}.\n\t *\n\t * @internal\n\t */\n\tprivate _registerMenuTree( componentView: MenuBarMenuView | MenuBarMenuListItemButtonView, parentMenuView: MenuBarMenuView ) {\n\t\tif ( !( componentView instanceof MenuBarMenuView ) ) {\n\t\t\tcomponentView.delegate( 'mouseenter' ).to( parentMenuView );\n\n\t\t\treturn;\n\t\t}\n\n\t\tthis.registerMenu( componentView, parentMenuView );\n\n\t\tconst menuBarItemsList = componentView.panelView.children\n\t\t\t.filter( child => child instanceof MenuBarMenuListView )[ 0 ] as MenuBarMenuListView | undefined;\n\n\t\tif ( !menuBarItemsList ) {\n\t\t\tcomponentView.delegate( 'mouseenter' ).to( parentMenuView );\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst nonSeparatorItems = menuBarItemsList.items.filter( item => item instanceof ListItemView ) as Array;\n\n\t\tfor ( const item of nonSeparatorItems ) {\n\t\t\tthis._registerMenuTree(\n\t\t\t\titem.children.get( 0 ) as MenuBarMenuView | MenuBarMenuListItemButtonView,\n\t\t\t\tcomponentView\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Manages the state of the {@link #isOpen} property of the menu bar. Because the state is a sum of individual\n\t * top-level menus' states, it's necessary to listen to their changes and update the state accordingly.\n\t *\n\t * Additionally, it prevents from unnecessary changes of `isOpen` when one top-level menu opens and another closes\n\t * (regardless of in which order), maintaining a stable `isOpen === true` in that situation.\n\t */\n\tprivate _setupIsOpenUpdater() {\n\t\tlet closeTimeout: ReturnType;\n\n\t\t// TODO: This is not the prettiest approach but at least it's simple.\n\t\tthis.on( 'menu:change:isOpen', ( evt, name, isOpen ) => {\n\t\t\tclearTimeout( closeTimeout );\n\n\t\t\tif ( isOpen ) {\n\t\t\t\tthis.isOpen = true;\n\t\t\t} else {\n\t\t\t\tcloseTimeout = setTimeout( () => {\n\t\t\t\t\tthis.isOpen = Array.from( this.children ).some( menuView => menuView.isOpen );\n\t\t\t\t}, 0 );\n\t\t\t}\n\t\t} );\n\t}\n}\n\nexport type MenuBarConfig = MenuBarConfigObject;\n\nexport type MenuBarConfigObject = {\n\titems?: Array;\n\tremoveItems?: Array;\n\taddItems?: Array;\n\tisVisible?: boolean;\n};\n\nexport type NormalizedMenuBarConfigObject = Required & {\n\tisUsingDefaultConfig: boolean;\n};\n\nexport type MenuBarMenuGroupDefinition = {\n\tgroupId: string;\n\titems: Array;\n};\n\nexport type MenuBarMenuDefinition = {\n\tmenuId: string;\n\tlabel: string;\n\tgroups: Array