` which\n * wraps the editable element and the toolbar. In {@link module:editor-inline/inlineeditor~InlineEditor}\n * it is the editable element itself (as there is no other wrapper). However, in\n * {@link module:editor-decoupled/decouplededitor~DecoupledEditor} it is set to `null` because this editor does not\n * come with a single \"main\" HTML element (its editable element and toolbar are separate).\n *\n * This property can be understood as a shorthand for retrieving the element that a specific editor integration\n * considers to be its main DOM element.\n */\n get element() {\n return null;\n }\n /**\n * Fires the {@link module:ui/editorui/editorui~EditorUI#event:update `update`} event.\n *\n * This method should be called when the editor UI (e.g. positions of its balloons) needs to be updated due to\n * some environmental change which CKEditor 5 is not aware of (e.g. resize of a container in which it is used).\n */\n update() {\n this.fire('update');\n }\n /**\n * Destroys the UI.\n */\n destroy() {\n this.stopListening();\n this.focusTracker.destroy();\n this.tooltipManager.destroy(this.editor);\n this.poweredBy.destroy();\n // Clean–up the references to the CKEditor instance stored in the native editable DOM elements.\n for (const domElement of this._editableElementsMap.values()) {\n domElement.ckeditorInstance = null;\n this.editor.keystrokes.stopListening(domElement);\n }\n this._editableElementsMap = new Map();\n this._focusableToolbarDefinitions = [];\n }\n /**\n * Stores the native DOM editable element used by the editor under a unique name.\n *\n * Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable\n * editable area, they can use the
Alt +
F10 keystroke to navigate over editor toolbars. See {@link #addToolbar}.\n *\n * @param rootName The unique name of the editable element.\n * @param domElement The native DOM editable element.\n */\n setEditableElement(rootName, domElement) {\n this._editableElementsMap.set(rootName, domElement);\n // Put a reference to the CKEditor instance in the editable native DOM element.\n // It helps 3rd–party software (browser extensions, other libraries) access and recognize\n // CKEditor 5 instances (editing roots) and use their API (there is no global editor\n // instance registry).\n if (!domElement.ckeditorInstance) {\n domElement.ckeditorInstance = this.editor;\n }\n // Register the element, so it becomes available for Alt+F10 and Esc navigation.\n this.focusTracker.add(domElement);\n const setUpKeystrokeHandler = () => {\n // The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).\n // Do not duplicate listeners.\n if (this.editor.editing.view.getDomRoot(rootName)) {\n return;\n }\n this.editor.keystrokes.listenTo(domElement);\n };\n // For editable elements set by features after EditorUI is ready (e.g. source editing).\n if (this.isReady) {\n setUpKeystrokeHandler();\n }\n // For editable elements set while the editor is being created (e.g. DOM roots).\n else {\n this.once('ready', setUpKeystrokeHandler);\n }\n }\n /**\n * Removes the editable from the editor UI. Removes all handlers added by {@link #setEditableElement}.\n *\n * @param rootName The name of the editable element to remove.\n */\n removeEditableElement(rootName) {\n const domElement = this._editableElementsMap.get(rootName);\n if (!domElement) {\n return;\n }\n this._editableElementsMap.delete(rootName);\n this.editor.keystrokes.stopListening(domElement);\n this.focusTracker.remove(domElement);\n domElement.ckeditorInstance = null;\n }\n /**\n * Returns the editable editor element with the given name or null if editable does not exist.\n *\n * @param rootName The editable name.\n */\n getEditableElement(rootName = 'main') {\n return this._editableElementsMap.get(rootName);\n }\n /**\n * Returns array of names of all editor editable elements.\n */\n getEditableElementsNames() {\n return this._editableElementsMap.keys();\n }\n /**\n * Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.\n *\n * Focusable toolbars can be accessed (focused) by users by pressing the
Alt +
F10 keystroke.\n * Successive keystroke presses navigate over available toolbars.\n *\n * @param toolbarView A instance of the toolbar to be registered.\n */\n addToolbar(toolbarView, options = {}) {\n if (toolbarView.isRendered) {\n this.focusTracker.add(toolbarView.element);\n this.editor.keystrokes.listenTo(toolbarView.element);\n }\n else {\n toolbarView.once('render', () => {\n this.focusTracker.add(toolbarView.element);\n this.editor.keystrokes.listenTo(toolbarView.element);\n });\n }\n this._focusableToolbarDefinitions.push({ toolbarView, options });\n }\n /**\n * Stores all editable elements used by the editor instance.\n *\n * @deprecated\n */\n get _editableElements() {\n /**\n * The {@link module:ui/editorui/editorui~EditorUI#_editableElements `EditorUI#_editableElements`} property has been\n * deprecated and will be removed in the near future. Please use\n * {@link module:ui/editorui/editorui~EditorUI#setEditableElement `setEditableElement()`} and\n * {@link module:ui/editorui/editorui~EditorUI#getEditableElement `getEditableElement()`} methods instead.\n *\n * @error editor-ui-deprecated-editable-elements\n * @param editorUI Editor UI instance the deprecated property belongs to.\n */\n console.warn('editor-ui-deprecated-editable-elements: ' +\n 'The EditorUI#_editableElements property has been deprecated and will be removed in the near future.', { editorUI: this });\n return this._editableElementsMap;\n }\n /**\n * Returns viewport offsets object:\n *\n * ```js\n * {\n * \ttop: Number,\n * \tright: Number,\n * \tbottom: Number,\n * \tleft: Number\n * }\n * ```\n *\n * Only top property is currently supported.\n */\n _readViewportOffsetFromConfig() {\n const editor = this.editor;\n const viewportOffsetConfig = editor.config.get('ui.viewportOffset');\n if (viewportOffsetConfig) {\n return viewportOffsetConfig;\n }\n // Not present in EditorConfig type, because it's legacy. Hence the `as` expression.\n const legacyOffsetConfig = editor.config.get('toolbar.viewportTopOffset');\n // Fall back to deprecated toolbar config.\n if (legacyOffsetConfig) {\n /**\n * The {@link module:core/editor/editorconfig~EditorConfig#toolbar `EditorConfig#toolbar.viewportTopOffset`}\n * property has been deprecated and will be removed in the near future. Please use\n * {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset`} instead.\n *\n * @error editor-ui-deprecated-viewport-offset-config\n */\n console.warn('editor-ui-deprecated-viewport-offset-config: ' +\n 'The `toolbar.vieportTopOffset` configuration option is deprecated. ' +\n 'It will be removed from future CKEditor versions. Use `ui.viewportOffset.top` instead.');\n return { top: legacyOffsetConfig };\n }\n // More keys to come in the future.\n return { top: 0 };\n }\n /**\n * Starts listening for
Alt +
F10 and
Esc keystrokes in the context of focusable\n * {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}\n * to allow users navigate across the UI.\n */\n _initFocusTracking() {\n const editor = this.editor;\n const editingView = editor.editing.view;\n let lastFocusedForeignElement;\n let candidateDefinitions;\n // Focus the next focusable toolbar on
Alt +
F10.\n editor.keystrokes.set('Alt+F10', (data, cancel) => {\n const focusedElement = this.focusTracker.focusedElement;\n // Focus moved out of a DOM element that\n // * is not a toolbar,\n // * does not belong to the editing view (e.g. source editing).\n if (Array.from(this._editableElementsMap.values()).includes(focusedElement) &&\n !Array.from(editingView.domRoots.values()).includes(focusedElement)) {\n lastFocusedForeignElement = focusedElement;\n }\n const currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();\n // * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.\n // This ensures, the navigation works always the same and no pair of toolbars takes over\n // (e.g. image and table toolbars when a selected image is inside a cell).\n // * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,\n // there were no candidates so they must be obtained (#12339).\n if (!currentFocusedToolbarDefinition || !candidateDefinitions) {\n candidateDefinitions = this._getFocusableCandidateToolbarDefinitions();\n }\n // In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.\n // This prevents an infinite loop.\n for (let i = 0; i < candidateDefinitions.length; i++) {\n const candidateDefinition = candidateDefinitions.shift();\n // Put the first definition to the back of the array. This allows circular navigation over all toolbars\n // on successive presses of Alt+F10.\n candidateDefinitions.push(candidateDefinition);\n // Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the\n // first item as per ToolbarView#focus() if the user navigated inside the toolbar.\n if (candidateDefinition !== currentFocusedToolbarDefinition &&\n this._focusFocusableCandidateToolbar(candidateDefinition)) {\n // Clean up after a current visible toolbar when switching to the next one.\n if (currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur) {\n currentFocusedToolbarDefinition.options.afterBlur();\n }\n break;\n }\n }\n cancel();\n });\n // Blur the focused toolbar on
Esc and bring the focus back to its origin.\n editor.keystrokes.set('Esc', (data, cancel) => {\n const focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();\n if (!focusedToolbarDef) {\n return;\n }\n // Bring focus back to where it came from before focusing the toolbar:\n // 1. If it came from outside the engine view (e.g. source editing), move it there.\n if (lastFocusedForeignElement) {\n lastFocusedForeignElement.focus();\n lastFocusedForeignElement = null;\n }\n // 2. There are two possibilities left:\n // 2.1. It could be that the focus went from an editable element in the view (root or nested).\n // 2.2. It could be the focus went straight to the toolbar before even focusing the editing area.\n // In either case, just focus the view editing. The focus will land where it belongs.\n else {\n editor.editing.view.focus();\n }\n // Clean up after the toolbar if there is anything to do there.\n if (focusedToolbarDef.options.afterBlur) {\n focusedToolbarDef.options.afterBlur();\n }\n cancel();\n });\n }\n /**\n * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.\n *\n * Focusable toolbars candidates are either:\n * * already visible,\n * * have `beforeFocus()` set in their {@link module:ui/editorui/editorui~FocusableToolbarDefinition definition} that suggests that\n * they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible\n * at this stage because it depends on its implementation, that in turn depends on the editing context (selection).\n *\n * **Note**: Contextual toolbars take precedence over regular toolbars.\n */\n _getFocusableCandidateToolbarDefinitions() {\n const definitions = [];\n for (const toolbarDef of this._focusableToolbarDefinitions) {\n const { toolbarView, options } = toolbarDef;\n if (isVisible(toolbarView.element) || options.beforeFocus) {\n definitions.push(toolbarDef);\n }\n }\n // Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.\n // For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.\n definitions.sort((defA, defB) => getToolbarDefinitionWeight(defA) - getToolbarDefinitionWeight(defB));\n return definitions;\n }\n /**\n * Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).\n *\n * `null` is returned when no toolbar is currently focused.\n */\n _getCurrentFocusedToolbarDefinition() {\n for (const definition of this._focusableToolbarDefinitions) {\n if (definition.toolbarView.element && definition.toolbarView.element.contains(this.focusTracker.focusedElement)) {\n return definition;\n }\n }\n return null;\n }\n /**\n * Focuses a focusable toolbar candidate using its definition.\n *\n * @param candidateToolbarDefinition A definition of the toolbar to focus.\n * @returns `true` when the toolbar candidate was focused. `false` otherwise.\n */\n _focusFocusableCandidateToolbar(candidateToolbarDefinition) {\n const { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;\n if (beforeFocus) {\n beforeFocus();\n }\n // If it didn't show up after beforeFocus(), it's not focusable at all.\n if (!isVisible(toolbarView.element)) {\n return false;\n }\n toolbarView.focus();\n return true;\n }\n /**\n * Provides an integration between {@link #viewportOffset} and {@link module:utils/dom/scroll~scrollViewportToShowTarget}.\n * It allows the UI-agnostic engine method to consider user-configured viewport offsets specific for the integration.\n *\n * @param evt The `scrollToTheSelection` event info.\n * @param data The payload carried by the `scrollToTheSelection` event.\n */\n _handleScrollToTheSelection(evt, data) {\n const configuredViewportOffset = {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0,\n ...this.viewportOffset\n };\n data.viewportOffset.top += configuredViewportOffset.top;\n data.viewportOffset.bottom += configuredViewportOffset.bottom;\n data.viewportOffset.left += configuredViewportOffset.left;\n data.viewportOffset.right += configuredViewportOffset.right;\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) {\n const { toolbarView, options } = toolbarDef;\n let weight = 10;\n // Prioritize already visible toolbars. They should get focused first.\n if (isVisible(toolbarView.element)) {\n weight--;\n }\n // Prioritize contextual toolbars. They are displayed at the selection.\n if (options.isContextual) {\n weight--;\n }\n return weight;\n}\n","import api from \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 * @module ui/editorui/editoruiview\n */\nimport View from '../view.js';\nimport BodyCollection from './bodycollection.js';\nimport '../../theme/components/editorui/editorui.css';\n/**\n * The editor UI view class. Base class for the editor main views.\n */\nexport default class EditorUIView extends View {\n /**\n * Creates an instance of the editor UI view class.\n *\n * @param locale The locale instance.\n */\n constructor(locale) {\n super(locale);\n this.body = new BodyCollection(locale);\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n this.body.attachToDom();\n }\n /**\n * @inheritDoc\n */\n destroy() {\n this.body.detachFromDom();\n return super.destroy();\n }\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 * @module ui/editorui/boxed/boxededitoruiview\n */\nimport EditorUIView from '../editoruiview.js';\nimport LabelView from '../../label/labelview.js';\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 class BoxedEditorUIView extends EditorUIView {\n /**\n * Creates an instance of the boxed editor UI view class.\n *\n * @param locale The locale instance..\n */\n constructor(locale) {\n super(locale);\n this.top = this.createCollection();\n this.main = this.createCollection();\n this._voiceLabelView = this._createVoiceLabel();\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-reset',\n 'ck-editor',\n 'ck-rounded-corners'\n ],\n role: 'application',\n dir: locale.uiLanguageDirection,\n lang: locale.uiLanguage,\n 'aria-labelledby': this._voiceLabelView.id\n },\n children: [\n this._voiceLabelView,\n {\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-editor__top',\n 'ck-reset_all'\n ],\n role: 'presentation'\n },\n children: this.top\n },\n {\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-editor__main'\n ],\n role: 'presentation'\n },\n children: this.main\n }\n ]\n });\n }\n /**\n * Creates a voice label view instance.\n */\n _createVoiceLabel() {\n const t = this.t;\n const voiceLabel = new LabelView();\n voiceLabel.text = t('Rich Text Editor');\n voiceLabel.extendTemplate({\n attributes: {\n class: 'ck-voice-label'\n }\n });\n return voiceLabel;\n }\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 * @module ui/editableui/editableuiview\n */\nimport View from '../view.js';\n/**\n * The editable UI view class.\n */\nexport default class EditableUIView extends View {\n /**\n * Creates an instance of EditableUIView class.\n *\n * @param locale The locale instance.\n * @param editingView The editing view instance the editable is related to.\n * @param editableElement The editable element. If not specified, this view\n * should create it. Otherwise, the existing element should be used.\n */\n constructor(locale, editingView, editableElement) {\n super(locale);\n /**\n * The name of the editable UI view.\n */\n this.name = null;\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-content',\n 'ck-editor__editable',\n 'ck-rounded-corners'\n ],\n lang: locale.contentLanguage,\n dir: locale.contentLanguageDirection\n }\n });\n this.set('isFocused', false);\n this._editableElement = editableElement;\n this._hasExternalElement = !!this._editableElement;\n this._editingView = editingView;\n }\n /**\n * Renders the view by either applying the {@link #template} to the existing\n * {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement} or assigning {@link #element}\n * as {@link module:ui/editableui/editableuiview~EditableUIView#_editableElement}.\n */\n render() {\n super.render();\n if (this._hasExternalElement) {\n this.template.apply(this.element = this._editableElement);\n }\n else {\n this._editableElement = this.element;\n }\n this.on('change:isFocused', () => this._updateIsFocusedClasses());\n this._updateIsFocusedClasses();\n }\n /**\n * @inheritDoc\n */\n destroy() {\n if (this._hasExternalElement) {\n this.template.revert(this._editableElement);\n }\n super.destroy();\n }\n /**\n * Whether an external {@link #_editableElement} was passed into the constructor, which also means\n * the view will not render its {@link #template}.\n */\n get hasExternalElement() {\n return this._hasExternalElement;\n }\n /**\n * Updates the `ck-focused` and `ck-blurred` CSS classes on the {@link #element} according to\n * the {@link #isFocused} property value using the {@link #_editingView editing view} API.\n */\n _updateIsFocusedClasses() {\n const editingView = this._editingView;\n if (editingView.isRenderingInProgress) {\n updateAfterRender(this);\n }\n else {\n update(this);\n }\n function update(view) {\n editingView.change(writer => {\n const viewRoot = editingView.document.getRoot(view.name);\n writer.addClass(view.isFocused ? 'ck-focused' : 'ck-blurred', viewRoot);\n writer.removeClass(view.isFocused ? 'ck-blurred' : 'ck-focused', viewRoot);\n });\n }\n // In a case of a multi-root editor, a callback will be attached more than once (one callback for each root).\n // While executing one callback the `isRenderingInProgress` observable is changing what causes executing another\n // callback and render is called inside the already pending render.\n // We need to be sure that callback is executed only when the value has changed from `true` to `false`.\n // See https://github.com/ckeditor/ckeditor5/issues/1676.\n function updateAfterRender(view) {\n editingView.once('change:isRenderingInProgress', (evt, name, value) => {\n if (!value) {\n update(view);\n }\n else {\n updateAfterRender(view);\n }\n });\n }\n }\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 * @module ui/editableui/inline/inlineeditableuiview\n */\nimport EditableUIView from '../editableuiview.js';\n/**\n * The inline editable UI class implementing an inline {@link module:ui/editableui/editableuiview~EditableUIView}.\n */\nexport default class InlineEditableUIView extends EditableUIView {\n /**\n * Creates an instance of the InlineEditableUIView class.\n *\n * @param locale The locale instance.\n * @param editingView The editing view instance the editable is related to.\n * @param editableElement The editable element. If not specified, the\n * {@link module:ui/editableui/editableuiview~EditableUIView}\n * will create it. Otherwise, the existing element will be used.\n * @param options Additional configuration of the view.\n * @param options.label A function that gets called with the instance of this view as an argument\n * and should return a string that represents the label of the editable for assistive technologies. If not provided,\n * a default label generator is used.\n */\n constructor(locale, editingView, editableElement, options = {}) {\n super(locale, editingView, editableElement);\n const t = locale.t;\n this.extendTemplate({\n attributes: {\n role: 'textbox',\n class: 'ck-editor__editable_inline'\n }\n });\n this._generateLabel = options.label || (() => t('Editor editing area: %0', this.name));\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n const editingView = this._editingView;\n editingView.change(writer => {\n const viewRoot = editingView.document.getRoot(this.name);\n writer.setAttribute('aria-label', this._generateLabel(this), viewRoot);\n });\n }\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 * @module ui/notification/notification\n */\n/* globals window */\nimport { ContextPlugin } from '@ckeditor/ckeditor5-core';\n/**\n * The Notification plugin.\n *\n * This plugin sends a few types of notifications: `success`, `info` and `warning`. The notifications need to be\n * handled and displayed by a plugin responsible for showing the UI of the notifications. Using this plugin for dispatching\n * notifications makes it possible to switch the notifications UI.\n *\n * Note that every unhandled and not stopped `warning` notification will be displayed as a system alert.\n * See {@link module:ui/notification/notification~Notification#showWarning}.\n */\nexport default class Notification extends ContextPlugin {\n /**\n * @inheritDoc\n */\n static get pluginName() {\n return 'Notification';\n }\n /**\n * @inheritDoc\n */\n init() {\n // Each unhandled and not stopped `show:warning` event is displayed as a system alert.\n this.on('show:warning', (evt, data) => {\n window.alert(data.message); // eslint-disable-line no-alert\n }, { priority: 'lowest' });\n }\n /**\n * Shows a success notification.\n *\n * By default, it fires the {@link #event:show:success `show:success` event} with the given `data`. The event namespace can be extended\n * using the `data.namespace` option. For example:\n *\n * ```ts\n * showSuccess( 'Image is uploaded.', {\n * \tnamespace: 'upload:image'\n * } );\n * ```\n *\n * will fire the `show:success:upload:image` event.\n *\n * You can provide the title of the notification:\n *\n * ```ts\n * showSuccess( 'Image is uploaded.', {\n * \ttitle: 'Image upload success'\n * } );\n * ```\n *\n * @param message The content of the notification.\n * @param data Additional data.\n * @param data.namespace Additional event namespace.\n * @param data.title The title of the notification.\n */\n showSuccess(message, data = {}) {\n this._showNotification({\n message,\n type: 'success',\n namespace: data.namespace,\n title: data.title\n });\n }\n /**\n * Shows an information notification.\n *\n * By default, it fires the {@link #event:show:info `show:info` event} with the given `data`. The event namespace can be extended\n * using the `data.namespace` option. For example:\n *\n * ```ts\n * showInfo( 'Editor is offline.', {\n * \tnamespace: 'editor:status'\n * } );\n * ```\n *\n * will fire the `show:info:editor:status` event.\n *\n * You can provide the title of the notification:\n *\n * ```ts\n * showInfo( 'Editor is offline.', {\n * \ttitle: 'Network information'\n * } );\n * ```\n *\n * @param message The content of the notification.\n * @param data Additional data.\n * @param data.namespace Additional event namespace.\n * @param data.title The title of the notification.\n */\n showInfo(message, data = {}) {\n this._showNotification({\n message,\n type: 'info',\n namespace: data.namespace,\n title: data.title\n });\n }\n /**\n * Shows a warning notification.\n *\n * By default, it fires the {@link #event:show:warning `show:warning` event}\n * with the given `data`. The event namespace can be extended using the `data.namespace` option. For example:\n *\n * ```ts\n * showWarning( 'Image upload error.', {\n * \tnamespace: 'upload:image'\n * } );\n * ```\n *\n * will fire the `show:warning:upload:image` event.\n *\n * You can provide the title of the notification:\n *\n * ```ts\n * showWarning( 'Image upload error.', {\n * \ttitle: 'Upload failed'\n * } );\n * ```\n *\n * Note that each unhandled and not stopped `warning` notification will be displayed as a system alert.\n * The plugin responsible for displaying warnings should `stop()` the event to prevent displaying it as an alert:\n *\n * ```ts\n * notifications.on( 'show:warning', ( evt, data ) => {\n * \t// Do something with the data.\n *\n * \t// Stop this event to prevent displaying it as an alert.\n * \tevt.stop();\n * } );\n * ```\n *\n * You can attach many listeners to the same event and `stop()` this event in a listener with a low priority:\n *\n * ```ts\n * notifications.on( 'show:warning', ( evt, data ) => {\n * \t// Show the warning in the UI, but do not stop it.\n * } );\n *\n * notifications.on( 'show:warning', ( evt, data ) => {\n * \t// Log the warning to some error tracker.\n *\n * \t// Stop this event to prevent displaying it as an alert.\n * \tevt.stop();\n * }, { priority: 'low' } );\n * ```\n *\n * @param message The content of the notification.\n * @param data Additional data.\n * @param data.namespace Additional event namespace.\n * @param data.title The title of the notification.\n */\n showWarning(message, data = {}) {\n this._showNotification({\n message,\n type: 'warning',\n namespace: data.namespace,\n title: data.title\n });\n }\n /**\n * Fires the `show` event with the specified type, namespace and message.\n *\n * @param data The message data.\n * @param data.message The content of the notification.\n * @param data.type The type of the message.\n * @param data.namespace Additional event namespace.\n * @param data.title The title of the notification.\n */\n _showNotification(data) {\n const event = data.namespace ?\n `show:${data.type}:${data.namespace}` :\n `show:${data.type}`;\n this.fire(event, {\n message: data.message,\n type: data.type,\n title: data.title || ''\n });\n }\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 * @module ui/model\n */\nimport { ObservableMixin } from '@ckeditor/ckeditor5-utils';\nimport { extend } from 'lodash-es';\n/**\n * The base MVC model class.\n */\nexport default class Model extends ObservableMixin() {\n /**\n * Creates a new Model instance.\n *\n * @param attributes The model state attributes to be defined during the instance creation.\n * @param properties The (out of state) properties to be appended to the instance during creation.\n */\n constructor(attributes, properties) {\n super();\n // Extend this instance with the additional (out of state) properties.\n if (properties) {\n extend(this, properties);\n }\n // Initialize the attributes.\n if (attributes) {\n this.set(attributes);\n }\n }\n}\n","import api from \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 * @module ui/panel/balloon/contextualballoon\n */\nimport BalloonPanelView from './balloonpanelview.js';\nimport View from '../../view.js';\nimport ButtonView from '../../button/buttonview.js';\nimport { Plugin, icons } from '@ckeditor/ckeditor5-core';\nimport { CKEditorError, FocusTracker, Rect, toUnit } from '@ckeditor/ckeditor5-utils';\nimport '../../../theme/components/panel/balloonrotator.css';\nimport '../../../theme/components/panel/fakepanel.css';\nconst toPx = toUnit('px');\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 /**\n * @inheritDoc\n */\n static get pluginName() {\n return 'ContextualBalloon';\n }\n /**\n * @inheritDoc\n */\n constructor(editor) {\n super(editor);\n /**\n * The map of views and their stacks.\n */\n this._viewToStack = new Map();\n /**\n * The map of IDs and stacks.\n */\n this._idToStack = new Map();\n /**\n * The common balloon panel view.\n */\n this._view = null;\n /**\n * Rotator view embedded in the contextual balloon.\n * Displays the currently visible view in the balloon and provides navigation for switching stacks.\n */\n this._rotatorView = null;\n /**\n * Displays fake panels under the balloon panel view when multiple stacks are added to the balloon.\n */\n this._fakePanelsView = null;\n this.positionLimiter = () => {\n const view = this.editor.editing.view;\n const viewDocument = view.document;\n const editableElement = viewDocument.selection.editableElement;\n if (editableElement) {\n return view.domConverter.mapViewToDom(editableElement.root);\n }\n return null;\n };\n this.set('visibleView', null);\n this.set('_numberOfStacks', 0);\n this.set('_singleViewMode', false);\n }\n /**\n * @inheritDoc\n */\n destroy() {\n super.destroy();\n if (this._view) {\n this._view.destroy();\n }\n if (this._rotatorView) {\n this._rotatorView.destroy();\n }\n if (this._fakePanelsView) {\n this._fakePanelsView.destroy();\n }\n }\n /**\n * The common balloon panel view.\n */\n get view() {\n if (!this._view) {\n this._createPanelView();\n }\n return this._view;\n }\n /**\n * Returns `true` when the given view is in one of the stacks. Otherwise returns `false`.\n */\n hasView(view) {\n return Array.from(this._viewToStack.keys()).includes(view);\n }\n /**\n * Adds a new view to the stack and makes it visible if the current stack is visible\n * or it is the first view in the balloon.\n *\n * @param data The configuration of the view.\n * @param data.stackId The ID of the stack that the view is added to. Defaults to `'main'`.\n * @param data.view The content of the balloon.\n * @param data.position Positioning options.\n * @param data.balloonClassName An additional CSS class added to the {@link #view balloon} when visible.\n * @param data.withArrow Whether the {@link #view balloon} should be rendered with an arrow. Defaults to `true`.\n * @param data.singleViewMode Whether the view should be the only visible view even if other stacks were added. Defaults to `false`.\n */\n add(data) {\n if (!this._view) {\n this._createPanelView();\n }\n if (this.hasView(data.view)) {\n /**\n * Trying to add configuration of the same view more than once.\n *\n * @error contextualballoon-add-view-exist\n */\n throw new CKEditorError('contextualballoon-add-view-exist', [this, data]);\n }\n const stackId = data.stackId || 'main';\n // If new stack is added, creates it and show view from this stack.\n if (!this._idToStack.has(stackId)) {\n this._idToStack.set(stackId, new Map([[data.view, data]]));\n this._viewToStack.set(data.view, this._idToStack.get(stackId));\n this._numberOfStacks = this._idToStack.size;\n if (!this._visibleStack || data.singleViewMode) {\n this.showStack(stackId);\n }\n return;\n }\n const stack = this._idToStack.get(stackId);\n if (data.singleViewMode) {\n this.showStack(stackId);\n }\n // Add new view to the stack.\n stack.set(data.view, data);\n this._viewToStack.set(data.view, stack);\n // And display it if is added to the currently visible stack.\n if (stack === this._visibleStack) {\n this._showView(data);\n }\n }\n /**\n * Removes the given view from the stack. If the removed view was visible,\n * the view preceding it in the stack will become visible instead.\n * When there is no view in the stack, the next stack will be displayed.\n * When there are no more stacks, the balloon will hide.\n *\n * @param view A view to be removed from the balloon.\n */\n remove(view) {\n if (!this.hasView(view)) {\n /**\n * Trying to remove the configuration of the view not defined in the stack.\n *\n * @error contextualballoon-remove-view-not-exist\n */\n throw new CKEditorError('contextualballoon-remove-view-not-exist', [this, view]);\n }\n const stack = this._viewToStack.get(view);\n if (this._singleViewMode && this.visibleView === view) {\n this._singleViewMode = false;\n }\n // When visible view will be removed we need to show a preceding view or next stack\n // if a view is the only view in the stack.\n if (this.visibleView === view) {\n if (stack.size === 1) {\n if (this._idToStack.size > 1) {\n this._showNextStack();\n }\n else {\n this.view.hide();\n this.visibleView = null;\n this._rotatorView.hideView();\n }\n }\n else {\n this._showView(Array.from(stack.values())[stack.size - 2]);\n }\n }\n if (stack.size === 1) {\n this._idToStack.delete(this._getStackId(stack));\n this._numberOfStacks = this._idToStack.size;\n }\n else {\n stack.delete(view);\n }\n this._viewToStack.delete(view);\n }\n /**\n * Updates the position of the balloon using the position data of the first visible view in the stack.\n * When new position data is given, the position data of the currently visible view will be updated.\n *\n * @param position Position options.\n */\n updatePosition(position) {\n if (position) {\n this._visibleStack.get(this.visibleView).position = position;\n }\n this.view.pin(this._getBalloonPosition());\n this._fakePanelsView.updatePosition();\n }\n /**\n * Shows the last view from the stack of a given ID.\n */\n showStack(id) {\n this.visibleStack = id;\n const stack = this._idToStack.get(id);\n if (!stack) {\n /**\n * Trying to show a stack that does not exist.\n *\n * @error contextualballoon-showstack-stack-not-exist\n */\n throw new CKEditorError('contextualballoon-showstack-stack-not-exist', this);\n }\n if (this._visibleStack === stack) {\n return;\n }\n this._showView(Array.from(stack.values()).pop());\n }\n /**\n * Initializes view instances.\n */\n _createPanelView() {\n this._view = new BalloonPanelView(this.editor.locale);\n this.editor.ui.view.body.add(this._view);\n this.editor.ui.focusTracker.add(this._view.element);\n this._rotatorView = this._createRotatorView();\n this._fakePanelsView = this._createFakePanelsView();\n }\n /**\n * Returns the stack of the currently visible view.\n */\n get _visibleStack() {\n return this._viewToStack.get(this.visibleView);\n }\n /**\n * Returns the ID of the given stack.\n */\n _getStackId(stack) {\n const entry = Array.from(this._idToStack.entries()).find(entry => entry[1] === stack);\n return entry[0];\n }\n /**\n * Shows the last view from the next stack.\n */\n _showNextStack() {\n const stacks = Array.from(this._idToStack.values());\n let nextIndex = stacks.indexOf(this._visibleStack) + 1;\n if (!stacks[nextIndex]) {\n nextIndex = 0;\n }\n this.showStack(this._getStackId(stacks[nextIndex]));\n }\n /**\n * Shows the last view from the previous stack.\n */\n _showPrevStack() {\n const stacks = Array.from(this._idToStack.values());\n let nextIndex = stacks.indexOf(this._visibleStack) - 1;\n if (!stacks[nextIndex]) {\n nextIndex = stacks.length - 1;\n }\n this.showStack(this._getStackId(stacks[nextIndex]));\n }\n /**\n * Creates a rotator view.\n */\n _createRotatorView() {\n const view = new RotatorView(this.editor.locale);\n const t = this.editor.locale.t;\n this.view.content.add(view);\n // Hide navigation when there is only a one stack & not in single view mode.\n view.bind('isNavigationVisible').to(this, '_numberOfStacks', this, '_singleViewMode', (value, isSingleViewMode) => {\n return !isSingleViewMode && value > 1;\n });\n // Update balloon position after toggling navigation.\n view.on('change:isNavigationVisible', () => (this.updatePosition()), { priority: 'low' });\n // Update stacks counter value.\n view.bind('counter').to(this, 'visibleView', this, '_numberOfStacks', (visibleView, numberOfStacks) => {\n if (numberOfStacks < 2) {\n return '';\n }\n const current = Array.from(this._idToStack.values()).indexOf(this._visibleStack) + 1;\n return t('%0 of %1', [current, numberOfStacks]);\n });\n view.buttonNextView.on('execute', () => {\n // When current view has a focus then move focus to the editable before removing it,\n // otherwise editor will lost focus.\n if (view.focusTracker.isFocused) {\n this.editor.editing.view.focus();\n }\n this._showNextStack();\n });\n view.buttonPrevView.on('execute', () => {\n // When current view has a focus then move focus to the editable before removing it,\n // otherwise editor will lost focus.\n if (view.focusTracker.isFocused) {\n this.editor.editing.view.focus();\n }\n this._showPrevStack();\n });\n return view;\n }\n /**\n * Creates a fake panels view.\n */\n _createFakePanelsView() {\n const view = new FakePanelsView(this.editor.locale, this.view);\n view.bind('numberOfPanels').to(this, '_numberOfStacks', this, '_singleViewMode', (number, isSingleViewMode) => {\n const showPanels = !isSingleViewMode && number >= 2;\n return showPanels ? Math.min(number - 1, 2) : 0;\n });\n view.listenTo(this.view, 'change:top', () => view.updatePosition());\n view.listenTo(this.view, 'change:left', () => view.updatePosition());\n this.editor.ui.view.body.add(view);\n return view;\n }\n /**\n * Sets the view as the content of the balloon and attaches the balloon using position\n * options of the first view.\n *\n * @param data Configuration.\n * @param data.view The view to show in the balloon.\n * @param data.balloonClassName Additional class name which will be added to the {@link #view balloon}.\n * @param data.withArrow Whether the {@link #view balloon} should be rendered with an arrow.\n */\n _showView({ view, balloonClassName = '', withArrow = true, singleViewMode = false }) {\n this.view.class = balloonClassName;\n this.view.withArrow = withArrow;\n this._rotatorView.showView(view);\n this.visibleView = view;\n this.view.pin(this._getBalloonPosition());\n this._fakePanelsView.updatePosition();\n if (singleViewMode) {\n this._singleViewMode = true;\n }\n }\n /**\n * Returns position options of the last view in the stack.\n * This keeps the balloon in the same position when the view is changed.\n */\n _getBalloonPosition() {\n let position = Array.from(this._visibleStack.values()).pop().position;\n if (position) {\n // Use the default limiter if none has been specified.\n if (!position.limiter) {\n // Don't modify the original options object.\n position = Object.assign({}, position, {\n limiter: this.positionLimiter\n });\n }\n // Don't modify the original options object.\n position = Object.assign({}, position, {\n viewportOffsetConfig: this.editor.ui.viewportOffset\n });\n }\n return position;\n }\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 /**\n * @inheritDoc\n */\n constructor(locale) {\n super(locale);\n const t = locale.t;\n const bind = this.bindTemplate;\n this.set('isNavigationVisible', true);\n this.focusTracker = new FocusTracker();\n this.buttonPrevView = this._createButtonView(t('Previous'), icons.previousArrow);\n this.buttonNextView = this._createButtonView(t('Next'), icons.nextArrow);\n this.content = this.createCollection();\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-balloon-rotator'\n ],\n 'z-index': '-1'\n },\n children: [\n {\n tag: 'div',\n attributes: {\n class: [\n 'ck-balloon-rotator__navigation',\n bind.to('isNavigationVisible', value => value ? '' : 'ck-hidden')\n ]\n },\n children: [\n this.buttonPrevView,\n {\n tag: 'span',\n attributes: {\n class: [\n 'ck-balloon-rotator__counter'\n ]\n },\n children: [\n {\n text: bind.to('counter')\n }\n ]\n },\n this.buttonNextView\n ]\n },\n {\n tag: 'div',\n attributes: {\n class: 'ck-balloon-rotator__content'\n },\n children: this.content\n }\n ]\n });\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n this.focusTracker.add(this.element);\n }\n /**\n * @inheritDoc\n */\n destroy() {\n super.destroy();\n this.focusTracker.destroy();\n }\n /**\n * Shows a given view.\n *\n * @param view The view to show.\n */\n showView(view) {\n this.hideView();\n this.content.add(view);\n }\n /**\n * Hides the currently displayed view.\n */\n hideView() {\n this.content.clear();\n }\n /**\n * Creates a navigation button view.\n *\n * @param label The button label.\n * @param icon The button icon.\n */\n _createButtonView(label, icon) {\n const view = new ButtonView(this.locale);\n view.set({\n label,\n icon,\n tooltip: true\n });\n return view;\n }\n}\n/**\n * Displays additional layers under the balloon when multiple stacks are added to the balloon.\n */\nclass FakePanelsView extends View {\n /**\n * @inheritDoc\n */\n constructor(locale, balloonPanelView) {\n super(locale);\n const bind = this.bindTemplate;\n this.set('top', 0);\n this.set('left', 0);\n this.set('height', 0);\n this.set('width', 0);\n this.set('numberOfPanels', 0);\n this.content = this.createCollection();\n this._balloonPanelView = balloonPanelView;\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck-fake-panel',\n bind.to('numberOfPanels', number => number ? '' : 'ck-hidden')\n ],\n style: {\n top: bind.to('top', toPx),\n left: bind.to('left', toPx),\n width: bind.to('width', toPx),\n height: bind.to('height', toPx)\n }\n },\n children: this.content\n });\n this.on('change:numberOfPanels', (evt, name, next, prev) => {\n if (next > prev) {\n this._addPanels(next - prev);\n }\n else {\n this._removePanels(prev - next);\n }\n this.updatePosition();\n });\n }\n _addPanels(number) {\n while (number--) {\n const view = new View();\n view.setTemplate({ tag: 'div' });\n this.content.add(view);\n this.registerChild(view);\n }\n }\n _removePanels(number) {\n while (number--) {\n const view = this.content.last;\n this.content.remove(view);\n this.deregisterChild(view);\n view.destroy();\n }\n }\n /**\n * Updates coordinates of fake panels.\n */\n updatePosition() {\n if (this.numberOfPanels) {\n const { top, left } = this._balloonPanelView;\n const { width, height } = new Rect(this._balloonPanelView.element);\n Object.assign(this, { top, left, width, height });\n }\n }\n}\n","import api from \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 * @module ui/panel/sticky/stickypanelview\n */\nimport View from '../../view.js';\nimport Template from '../../template.js';\nimport { global, toUnit, Rect } from '@ckeditor/ckeditor5-utils';\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' );\nimport '../../../theme/components/panel/stickypanel.css';\nconst toPx = toUnit('px');\n/**\n * The sticky panel view class.\n */\nexport default class StickyPanelView extends View {\n /**\n * @inheritDoc\n */\n constructor(locale) {\n super(locale);\n const bind = this.bindTemplate;\n this.set('isActive', false);\n this.set('isSticky', false);\n this.set('limiterElement', null);\n this.set('limiterBottomOffset', 50);\n this.set('viewportTopOffset', 0);\n this.set('_marginLeft', null);\n this.set('_isStickyToTheBottomOfLimiter', false);\n this.set('_stickyTopOffset', null);\n this.set('_stickyBottomOffset', null);\n this.content = this.createCollection();\n this._contentPanelPlaceholder = new Template({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-sticky-panel__placeholder'\n ],\n style: {\n display: bind.to('isSticky', isSticky => isSticky ? 'block' : 'none'),\n height: bind.to('isSticky', isSticky => {\n return isSticky ? toPx(this._contentPanelRect.height) : null;\n })\n }\n }\n }).render();\n this.contentPanelElement = new Template({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-sticky-panel__content',\n // Toggle class of the panel when \"sticky\" state changes in the view.\n bind.if('isSticky', 'ck-sticky-panel__content_sticky'),\n bind.if('_isStickyToTheBottomOfLimiter', 'ck-sticky-panel__content_sticky_bottom-limit')\n ],\n style: {\n width: bind.to('isSticky', isSticky => {\n return isSticky ? toPx(this._contentPanelPlaceholder.getBoundingClientRect().width) : null;\n }),\n top: bind.to('_stickyTopOffset', value => value ? toPx(value) : value),\n bottom: bind.to('_stickyBottomOffset', value => value ? toPx(value) : value),\n marginLeft: bind.to('_marginLeft')\n }\n },\n children: this.content\n }).render();\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-sticky-panel'\n ]\n },\n children: [\n this._contentPanelPlaceholder,\n this.contentPanelElement\n ]\n });\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n // Check if the panel should go into the sticky state immediately.\n this.checkIfShouldBeSticky();\n // Update sticky state of the panel as the window and ancestors are being scrolled.\n this.listenTo(global.document, 'scroll', () => {\n this.checkIfShouldBeSticky();\n }, { useCapture: true });\n // Synchronize with `model.isActive` because sticking an inactive panel is pointless.\n this.listenTo(this, 'change:isActive', () => {\n this.checkIfShouldBeSticky();\n });\n }\n /**\n * Analyzes the environment to decide whether the panel should be sticky or not.\n * Then handles the positioning of the panel.\n */\n checkIfShouldBeSticky() {\n // @if CK_DEBUG_STICKYPANEL // RectDrawer.clear();\n if (!this.limiterElement || !this.isActive) {\n this._unstick();\n return;\n }\n const limiterRect = new Rect(this.limiterElement);\n let visibleLimiterRect = limiterRect.getVisible();\n if (visibleLimiterRect) {\n const windowRect = new Rect(global.window);\n windowRect.top += this.viewportTopOffset;\n windowRect.height -= this.viewportTopOffset;\n visibleLimiterRect = visibleLimiterRect.getIntersection(windowRect);\n }\n // @if CK_DEBUG_STICKYPANEL // if ( visibleLimiterRect ) {\n // @if CK_DEBUG_STICKYPANEL // \tRectDrawer.draw( visibleLimiterRect,\n // @if CK_DEBUG_STICKYPANEL // \t\t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'red', outlineOffset: '-3px' },\n // @if CK_DEBUG_STICKYPANEL // \t\t'Visible anc'\n // @if CK_DEBUG_STICKYPANEL // \t);\n // @if CK_DEBUG_STICKYPANEL // }\n // @if CK_DEBUG_STICKYPANEL //\n // @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( limiterRect,\n // @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'green', outlineOffset: '-3px' },\n // @if CK_DEBUG_STICKYPANEL // \t'Limiter'\n // @if CK_DEBUG_STICKYPANEL // );\n // Stick the panel only if\n // * the limiter's ancestors are intersecting with each other so that some of their rects are visible,\n // * and the limiter's top edge is above the visible ancestors' top edge.\n if (visibleLimiterRect && limiterRect.top < visibleLimiterRect.top) {\n // @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( visibleLimiterRect,\n // @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '3px', opacity: '.8', outlineColor: 'fuchsia', outlineOffset: '-3px',\n // @if CK_DEBUG_STICKYPANEL // \t\tbackgroundColor: 'rgba(255, 0, 255, .3)' },\n // @if CK_DEBUG_STICKYPANEL // \t'Visible limiter'\n // @if CK_DEBUG_STICKYPANEL // );\n const visibleLimiterTop = visibleLimiterRect.top;\n // Check if there's a change the panel can be sticky to the bottom of the limiter.\n if (visibleLimiterTop + this._contentPanelRect.height + this.limiterBottomOffset > visibleLimiterRect.bottom) {\n const stickyBottomOffset = Math.max(limiterRect.bottom - visibleLimiterRect.bottom, 0) + this.limiterBottomOffset;\n // @if CK_DEBUG_STICKYPANEL // const stickyBottomOffsetRect = new Rect( {\n // @if CK_DEBUG_STICKYPANEL // \ttop: limiterRect.bottom - stickyBottomOffset, left: 0, right: 2000,\n // @if CK_DEBUG_STICKYPANEL // \tbottom: limiterRect.bottom - stickyBottomOffset, width: 2000, height: 1\n // @if CK_DEBUG_STICKYPANEL // } );\n // @if CK_DEBUG_STICKYPANEL // RectDrawer.draw( stickyBottomOffsetRect,\n // @if CK_DEBUG_STICKYPANEL // \t{ outlineWidth: '1px', opacity: '.8', outlineColor: 'black' },\n // @if CK_DEBUG_STICKYPANEL // \t'Sticky bottom offset'\n // @if CK_DEBUG_STICKYPANEL // );\n // Check if sticking the panel to the bottom of the limiter does not cause it to suddenly\n // move upwards if there's not enough space for it.\n if (limiterRect.bottom - stickyBottomOffset > limiterRect.top + this._contentPanelRect.height) {\n this._stickToBottomOfLimiter(stickyBottomOffset);\n }\n else {\n this._unstick();\n }\n }\n else {\n if (this._contentPanelRect.height + this.limiterBottomOffset < limiterRect.height) {\n this._stickToTopOfAncestors(visibleLimiterTop);\n }\n else {\n this._unstick();\n }\n }\n }\n else {\n this._unstick();\n }\n // @if CK_DEBUG_STICKYPANEL // console.clear();\n // @if CK_DEBUG_STICKYPANEL // console.log( 'isSticky', this.isSticky );\n // @if CK_DEBUG_STICKYPANEL // console.log( '_isStickyToTheBottomOfLimiter', this._isStickyToTheBottomOfLimiter );\n // @if CK_DEBUG_STICKYPANEL // console.log( '_stickyTopOffset', this._stickyTopOffset );\n // @if CK_DEBUG_STICKYPANEL // console.log( '_stickyBottomOffset', this._stickyBottomOffset );\n // @if CK_DEBUG_STICKYPANEL // if ( visibleLimiterRect ) {\n // @if CK_DEBUG_STICKYPANEL // \tRectDrawer.draw( visibleLimiterRect,\n // @if CK_DEBUG_STICKYPANEL // \t\t{ ...diagonalStylesBlack,\n // @if CK_DEBUG_STICKYPANEL // \t\t\toutlineWidth: '3px', opacity: '.8', outlineColor: 'orange', outlineOffset: '-3px',\n // @if CK_DEBUG_STICKYPANEL // \t\t\tbackgroundColor: 'rgba(0, 0, 255, .2)' },\n // @if CK_DEBUG_STICKYPANEL // \t\t'visibleLimiterRect'\n // @if CK_DEBUG_STICKYPANEL // \t);\n // @if CK_DEBUG_STICKYPANEL // }\n }\n /**\n * Sticks the panel at the given CSS `top` offset.\n *\n * @private\n * @param topOffset\n */\n _stickToTopOfAncestors(topOffset) {\n this.isSticky = true;\n this._isStickyToTheBottomOfLimiter = false;\n this._stickyTopOffset = topOffset;\n this._stickyBottomOffset = null;\n this._marginLeft = toPx(-global.window.scrollX);\n }\n /**\n * Sticks the panel at the bottom of the limiter with a given CSS `bottom` offset.\n *\n * @private\n * @param stickyBottomOffset\n */\n _stickToBottomOfLimiter(stickyBottomOffset) {\n this.isSticky = true;\n this._isStickyToTheBottomOfLimiter = true;\n this._stickyTopOffset = null;\n this._stickyBottomOffset = stickyBottomOffset;\n this._marginLeft = toPx(-global.window.scrollX);\n }\n /**\n * Unsticks the panel putting it back to its original position.\n *\n * @private\n */\n _unstick() {\n this.isSticky = false;\n this._isStickyToTheBottomOfLimiter = false;\n this._stickyTopOffset = null;\n this._stickyBottomOffset = null;\n this._marginLeft = null;\n }\n /**\n * Returns the bounding rect of the {@link #contentPanelElement}.\n *\n * @private\n */\n get _contentPanelRect() {\n return new Rect(this.contentPanelElement);\n }\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 * @module ui/search/text/searchtextqueryview\n */\nimport ButtonView from '../../button/buttonview.js';\nimport IconView from '../../icon/iconview.js';\nimport LabeledFieldView from '../../labeledfield/labeledfieldview.js';\nimport { createLabeledInputText } from '../../labeledfield/utils.js';\nimport { icons } from '@ckeditor/ckeditor5-core';\n/**\n * A search input field for the {@link module:ui/search/text/searchtextview~SearchTextView} component.\n *\n * @internal\n * @extends module:ui/labeledfield/labeledfieldview~LabeledFieldView\n */\nexport default class SearchTextQueryView extends LabeledFieldView {\n /**\n * @inheritDoc\n */\n constructor(locale, config) {\n const t = locale.t;\n const viewConfig = Object.assign({}, {\n showResetButton: true,\n showIcon: true,\n creator: createLabeledInputText\n }, config);\n super(locale, viewConfig.creator);\n this.label = config.label;\n this._viewConfig = viewConfig;\n if (this._viewConfig.showIcon) {\n this.iconView = new IconView();\n this.iconView.content = icons.loupe;\n this.fieldWrapperChildren.add(this.iconView, 0);\n this.extendTemplate({\n attributes: {\n class: 'ck-search__query_with-icon'\n }\n });\n }\n if (this._viewConfig.showResetButton) {\n this.resetButtonView = new ButtonView(locale);\n this.resetButtonView.set({\n label: t('Clear'),\n icon: icons.cancel,\n class: 'ck-search__reset',\n isVisible: false,\n tooltip: true\n });\n this.resetButtonView.on('execute', () => {\n this.reset();\n this.focus();\n this.fire('reset');\n });\n this.resetButtonView.bind('isVisible').to(this.fieldView, 'isEmpty', isEmpty => !isEmpty);\n this.fieldWrapperChildren.add(this.resetButtonView);\n this.extendTemplate({\n attributes: {\n class: 'ck-search__query_with-reset'\n }\n });\n }\n }\n /**\n * Resets the search field to its default state.\n */\n reset() {\n this.fieldView.reset();\n if (this._viewConfig.showResetButton) {\n this.resetButtonView.isVisible = false;\n }\n }\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 */\nimport View from '../view.js';\n/**\n * A view displaying an information text related to different states of {@link module:ui/search/text/searchtextview~SearchTextView}.\n *\n * @internal\n */\nexport default class SearchInfoView extends View {\n /**\n * @inheritDoc\n */\n constructor() {\n super();\n const bind = this.bindTemplate;\n this.set({\n isVisible: false,\n primaryText: '',\n secondaryText: ''\n });\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-search__info',\n bind.if('isVisible', 'ck-hidden', value => !value)\n ],\n tabindex: -1\n },\n children: [\n {\n tag: 'span',\n children: [\n {\n text: [bind.to('primaryText')]\n }\n ]\n },\n {\n tag: 'span',\n children: [\n {\n text: [bind.to('secondaryText')]\n }\n ]\n }\n ]\n });\n }\n /**\n * Focuses the view\n */\n focus() {\n this.element.focus();\n }\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 * @module ui/search/searchresultsview\n */\nimport View from '../view.js';\nimport { FocusTracker } from '@ckeditor/ckeditor5-utils';\nimport { default as FocusCycler } from '../focuscycler.js';\n/**\n * A sub-component of {@link module:ui/search/text/searchtextview~SearchTextView}. It hosts the filtered and the information views.\n */\nexport default class SearchResultsView extends View {\n /**\n * @inheritDoc\n */\n constructor(locale) {\n super(locale);\n this.children = this.createCollection();\n this.focusTracker = new FocusTracker();\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-search__results'\n ],\n tabindex: -1\n },\n children: this.children\n });\n this._focusCycler = new FocusCycler({\n focusables: this.children,\n focusTracker: this.focusTracker\n });\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n for (const child of this.children) {\n this.focusTracker.add(child.element);\n }\n }\n /**\n * Focuses the view.\n */\n focus() {\n this._focusCycler.focusFirst();\n }\n /**\n * Focuses the first child view.\n */\n focusFirst() {\n this._focusCycler.focusFirst();\n }\n /**\n * Focuses the last child view.\n */\n focusLast() {\n this._focusCycler.focusLast();\n }\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 \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 || {};","/**\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 * @module ui/search/text/searchtextview\n*/\nimport { FocusTracker, KeystrokeHandler } from '@ckeditor/ckeditor5-utils';\nimport View from '../../view.js';\nimport { default as SearchTextQueryView } from './searchtextqueryview.js';\nimport SearchInfoView from '../searchinfoview.js';\nimport SearchResultsView from '../searchresultsview.js';\nimport FocusCycler from '../../focuscycler.js';\nimport { escapeRegExp } from 'lodash-es';\nimport '../../../theme/components/search/search.css';\n/**\n * A search component that allows filtering of an arbitrary view based on a search query\n * specified by the user in a text field.\n *\n *```ts\n * // This view must specify the `filter()` and `focus()` methods.\n * const filteredView = ...;\n *\n * const searchView = new SearchTextView( locale, {\n * \tsearchFieldLabel: 'Search list items',\n * \tfilteredView\n * } );\n *\n * view.render();\n *\n * document.body.append( view.element );\n * ```\n */\nexport default class SearchTextView extends View {\n /**\n * Creates an instance of the {@link module:ui/search/text/searchtextview~SearchTextView} class.\n *\n * @param locale The localization services instance.\n * @param config Configuration of the view.\n */\n constructor(locale, config) {\n super(locale);\n this._config = config;\n this.filteredView = config.filteredView;\n this.queryView = this._createSearchTextQueryView();\n this.focusTracker = new FocusTracker();\n this.keystrokes = new KeystrokeHandler();\n this.resultsView = new SearchResultsView(locale);\n this.children = this.createCollection();\n this.focusableChildren = this.createCollection([this.queryView, this.resultsView]);\n this.set('isEnabled', true);\n this.set('resultsCount', 0);\n this.set('totalItemsCount', 0);\n if (config.infoView && config.infoView.instance) {\n this.infoView = config.infoView.instance;\n }\n else {\n this.infoView = new SearchInfoView();\n this._enableDefaultInfoViewBehavior();\n this.on('render', () => {\n // Initial search that determines if there are any searchable items\n // and displays the corresponding info text.\n this.search('');\n });\n }\n this.resultsView.children.addMany([this.infoView, this.filteredView]);\n this.focusCycler = new FocusCycler({\n focusables: this.focusableChildren,\n focusTracker: this.focusTracker,\n keystrokeHandler: this.keystrokes,\n actions: {\n // Navigate form fields backwards using the Shift + Tab keystroke.\n focusPrevious: 'shift + tab',\n // Navigate form fields forwards using the Tab key.\n focusNext: 'tab'\n }\n });\n this.on('search', (evt, { resultsCount, totalItemsCount }) => {\n this.resultsCount = resultsCount;\n this.totalItemsCount = totalItemsCount;\n });\n this.setTemplate({\n tag: 'div',\n attributes: {\n class: [\n 'ck',\n 'ck-search',\n config.class || null\n ],\n tabindex: '-1'\n },\n children: this.children\n });\n }\n /**\n * @inheritDoc\n */\n render() {\n super.render();\n this.children.addMany([\n this.queryView,\n this.resultsView\n ]);\n const stopPropagation = (data) => data.stopPropagation();\n for (const focusableChild of this.focusableChildren) {\n this.focusTracker.add(focusableChild.element);\n }\n // Start listening for the keystrokes coming from #element.\n this.keystrokes.listenTo(this.element);\n // Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's\n // keystroke handler would take over the key management in the URL input. We need to prevent\n // this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.\n this.keystrokes.set('arrowright', stopPropagation);\n this.keystrokes.set('arrowleft', stopPropagation);\n this.keystrokes.set('arrowup', stopPropagation);\n this.keystrokes.set('arrowdown', stopPropagation);\n }\n /**\n * Focuses the {@link #queryView}.\n */\n focus() {\n this.queryView.focus();\n }\n /**\n * Resets the component to its initial state.\n */\n reset() {\n this.queryView.reset();\n this.search('');\n }\n /**\n * Searches the {@link #filteredView} for the given query.\n *\n * @internal\n * @param query The search query string.\n */\n search(query) {\n const regExp = query ? new RegExp(escapeRegExp(query), 'ig') : null;\n const filteringResults = this.filteredView.filter(regExp);\n this.fire('search', { query, ...filteringResults });\n }\n /**\n * Creates a search field view based on configured creator..\n */\n _createSearchTextQueryView() {\n const queryView = new SearchTextQueryView(this.locale, this._config.queryView);\n this.listenTo(queryView.fieldView, 'input', () => {\n this.search(queryView.fieldView.element.value);\n });\n queryView.on('reset', () => this.reset());\n queryView.bind('isEnabled').to(this);\n return queryView;\n }\n /**\n * Initializes the default {@link #infoView} behavior with default text labels when no custom info view\n * was specified in the view config.\n */\n _enableDefaultInfoViewBehavior() {\n const t = this.locale.t;\n const infoView = this.infoView;\n this.on('search', (evt, data) => {\n if (!data.resultsCount) {\n const defaultTextConfig = this._config.infoView && this._config.infoView.text;\n let primaryText, secondaryText;\n if (data.totalItemsCount) {\n if (defaultTextConfig && defaultTextConfig.notFound) {\n primaryText = defaultTextConfig.notFound.primary;\n secondaryText = defaultTextConfig.notFound.secondary;\n }\n else {\n primaryText = t('No results found');\n secondaryText = '';\n }\n }\n else {\n if (defaultTextConfig && defaultTextConfig.noSearchableItems) {\n primaryText = defaultTextConfig.noSearchableItems.primary;\n secondaryText = defaultTextConfig.noSearchableItems.secondary;\n }\n else {\n primaryText = t('No searchable items');\n secondaryText = '';\n }\n }\n infoView.set({\n primaryText: normalizeInfoText(primaryText, data),\n secondaryText: normalizeInfoText(secondaryText, data),\n isVisible: true\n });\n }\n else {\n infoView.set({\n isVisible: false\n });\n }\n });\n function normalizeInfoText(text, { query, resultsCount, totalItemsCount }) {\n return typeof text === 'function' ? text(query, resultsCount, totalItemsCount) : text;\n }\n }\n}\n","import api from \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 * @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 * @module ui/autocomplete/autocompleteview\n*/\nimport { getOptimalPosition, global, toUnit, Rect } from '@ckeditor/ckeditor5-utils';\nimport SearchTextView from '../search/text/searchtextview.js';\nimport '../../theme/components/autocomplete/autocomplete.css';\n/**\n * The autocomplete component's view class. It extends the {@link module:ui/search/text/searchtextview~SearchTextView} class\n * with a floating {@link #resultsView} that shows up when the user starts typing and hides when they blur\n * the component.\n */\nclass AutocompleteView extends SearchTextView {\n /**\n * @inheritDoc\n */\n constructor(locale, config) {\n super(locale, config);\n this._config = config;\n const toPx = toUnit('px');\n this.extendTemplate({\n attributes: {\n class: ['ck-autocomplete']\n }\n });\n const bindResultsView = this.resultsView.bindTemplate;\n this.resultsView.set('isVisible', false);\n this.resultsView.set('_position', 's');\n this.resultsView.set('_width', 0);\n this.resultsView.extendTemplate({\n attributes: {\n class: [\n bindResultsView.if('isVisible', 'ck-hidden', value => !value),\n bindResultsView.to('_position', value => `ck-search__results_${value}`)\n ],\n style: {\n width: bindResultsView.to('_width', toPx)\n }\n }\n });\n // Update the visibility of the results view when the user focuses or blurs the component.\n // This is also integration for the `resetOnBlur` configuration.\n this.focusTracker.on('change:isFocused', (evt, name, isFocused) => {\n this._updateResultsVisibility();\n if (isFocused) {\n // Reset the scroll position of the results view whenever the autocomplete reopens.\n this.resultsView.element.scrollTop = 0;\n }\n else if (config.resetOnBlur) {\n this.queryView.reset();\n }\n });\n // Update the visibility of the results view when the user types in the query field.\n // This is an integration for `queryMinChars` configuration.\n // This is an integration for search results changing length and the #resultsView requiring to be repositioned.\n this.on('search', () => {\n this._updateResultsVisibility();\n this._updateResultsViewWidthAndPosition();\n });\n // Hide the results view when the user presses the ESC key.\n this.keystrokes.set('esc', (evt, cancel) => {\n // Let the DOM event pass through if the focus is in the query view.\n if (!this.resultsView.isVisible) {\n return;\n }\n // Focus the query view first and only then close the results view. Otherwise, if the focus\n // was in the results view, it will get lost.\n this.queryView.focus();\n this.resultsView.isVisible = false;\n cancel();\n });\n // Update the position of the results view when the user scrolls the page.\n // TODO: This needs to be debounced down the road.\n this.listenTo(global.document, 'scroll', () => {\n this._updateResultsViewWidthAndPosition();\n });\n // Hide the results when the component becomes disabled.\n this.on('change:isEnabled', () => {\n this._updateResultsVisibility();\n });\n // Update the value of the query field when the user selects a result.\n this.filteredView.on('execute', (evt, { value }) => {\n // Focus the query view first to avoid losing the focus.\n this.focus();\n // Resetting the view will ensure that the #queryView will update its empty state correctly.\n // This prevents bugs related to dynamic labels or auto-grow when re-setting the same value\n // to #queryView.fieldView.value (which does not trigger empty state change) to an\n // #queryView.fieldView.element that has been changed by the user.\n this.reset();\n // Update the value of the query field.\n this.queryView.fieldView.value = this.queryView.fieldView.element.value = value;\n // Finally, hide the results view. The focus has been moved earlier so this is safe.\n this.resultsView.isVisible = false;\n });\n // Update the position and width of the results view when it becomes visible.\n this.resultsView.on('change:isVisible', () => {\n this._updateResultsViewWidthAndPosition();\n });\n }\n /**\n * Updates the position of the results view on demand.\n */\n _updateResultsViewWidthAndPosition() {\n if (!this.resultsView.isVisible) {\n return;\n }\n this.resultsView._width = new Rect(this.queryView.fieldView.element).width;\n const optimalResultsPosition = AutocompleteView._getOptimalPosition({\n element: this.resultsView.element,\n target: this.queryView.element,\n fitInViewport: true,\n positions: AutocompleteView.defaultResultsPositions\n });\n // _getOptimalPosition will return null if there is no optimal position found (e.g. target is off the viewport).\n this.resultsView._position = optimalResultsPosition ? optimalResultsPosition.name : 's';\n }\n /**\n * Updates the visibility of the results view on demand.\n */\n _updateResultsVisibility() {\n const queryMinChars = typeof this._config.queryMinChars === 'undefined' ? 0 : this._config.queryMinChars;\n const queryLength = this.queryView.fieldView.element.value.length;\n this.resultsView.isVisible = this.focusTracker.isFocused && this.isEnabled && queryLength >= queryMinChars;\n }\n}\n/**\n * Positions for the autocomplete results view. Two positions are defined by default:\n * * `s` - below the search field,\n * * `n` - above the search field.\n */\nAutocompleteView.defaultResultsPositions = [\n (fieldRect => {\n return {\n top: fieldRect.bottom,\n left: fieldRect.left,\n name: 's'\n };\n }),\n ((fieldRect, resultsRect) => {\n return {\n top: fieldRect.top - resultsRect.height,\n left: fieldRect.left,\n name: 'n'\n };\n })\n];\n/**\n * A function used to calculate the optimal position for the dropdown panel.\n */\nAutocompleteView._getOptimalPosition = getOptimalPosition;\nexport default AutocompleteView;\n","/**\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 \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 || {};","/**\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 * @module ui/toolbar/balloon/balloontoolbar\n */\nimport ContextualBalloon from '../../panel/balloon/contextualballoon.js';\nimport ToolbarView from '../toolbarview.js';\nimport BalloonPanelView, { generatePositions } from '../../panel/balloon/balloonpanelview.js';\nimport normalizeToolbarConfig from '../normalizetoolbarconfig.js';\nimport { Plugin } from '@ckeditor/ckeditor5-core';\nimport { FocusTracker, Rect, ResizeObserver, env, global, toUnit } from '@ckeditor/ckeditor5-utils';\nimport { debounce } from 'lodash-es';\nconst toPx = toUnit('px');\n/**\n * The contextual toolbar.\n *\n * It uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.\n */\nexport default class BalloonToolbar extends Plugin {\n /**\n * @inheritDoc\n */\n static get pluginName() {\n return 'BalloonToolbar';\n }\n /**\n * @inheritDoc\n */\n static get requires() {\n return [ContextualBalloon];\n }\n /**\n * @inheritDoc\n */\n constructor(editor) {\n super(editor);\n /**\n * An instance of the resize observer that allows to respond to changes in editable's geometry\n * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).\n *\n * **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the\n * {@link module:core/editor/editorconfig~EditorConfig#balloonToolbar configuration}.\n *\n * **Note:** Created in {@link #init}.\n */\n this._resizeObserver = null;\n this._balloonConfig = normalizeToolbarConfig(editor.config.get('balloonToolbar'));\n this.toolbarView = this._createToolbarView();\n this.focusTracker = new FocusTracker();\n // Wait for the EditorUI#init. EditableElement is not available before.\n editor.ui.once('ready', () => {\n this.focusTracker.add(editor.ui.getEditableElement());\n this.focusTracker.add(this.toolbarView.element);\n });\n // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.\n editor.ui.addToolbar(this.toolbarView, {\n beforeFocus: () => this.show(true),\n afterBlur: () => this.hide(),\n isContextual: true\n });\n this._balloon = editor.plugins.get(ContextualBalloon);\n this._fireSelectionChangeDebounced = debounce(() => this.fire('_selectionChangeDebounced'), 200);\n // The appearance of the BalloonToolbar method is event–driven.\n // It is possible to stop the #show event and this prevent the toolbar from showing up.\n this.decorate('show');\n }\n /**\n * @inheritDoc\n */\n init() {\n const editor = this.editor;\n const selection = editor.model.document.selection;\n // Show/hide the toolbar on editable focus/blur.\n this.listenTo(this.focusTracker, 'change:isFocused', (evt, name, isFocused) => {\n const isToolbarVisible = this._balloon.visibleView === this.toolbarView;\n if (!isFocused && isToolbarVisible) {\n this.hide();\n }\n else if (isFocused) {\n this.show();\n }\n });\n // Hide the toolbar when the selection is changed by a direct change or has changed to collapsed.\n this.listenTo(selection, 'change:range', (evt, data) => {\n if (data.directChange || selection.isCollapsed) {\n this.hide();\n }\n // Fire internal `_selectionChangeDebounced` event to use it for showing\n // the toolbar after the selection stops changing.\n this._fireSelectionChangeDebounced();\n });\n // Show the toolbar when the selection stops changing.\n this.listenTo(this, '_selectionChangeDebounced', () => {\n if (this.editor.editing.view.document.isFocused) {\n this.show();\n }\n });\n if (!this._balloonConfig.shouldNotGroupWhenFull) {\n this.listenTo(editor, 'ready', () => {\n const editableElement = editor.ui.view.editable.element;\n // Set #toolbarView's max-width on the initialization and update it on the editable resize.\n this._resizeObserver = new ResizeObserver(editableElement, entry => {\n // The max-width equals 90% of the editable's width for the best user experience.\n // The value keeps the balloon very close to the boundaries of the editable and limits the cases\n // when the balloon juts out from the editable element it belongs to.\n this.toolbarView.maxWidth = toPx(entry.contentRect.width * .9);\n });\n });\n }\n // Listen to the toolbar view and whenever it changes its geometry due to some items being\n // grouped or ungrouped, update the position of the balloon because a shorter/longer toolbar\n // means the balloon could be pointing at the wrong place. Once updated, the balloon will point\n // at the right selection in the content again.\n // https://github.com/ckeditor/ckeditor5/issues/6444\n this.listenTo(this.toolbarView, 'groupedItemsUpdate', () => {\n this._updatePosition();\n });\n // Creates toolbar components based on given configuration.\n // This needs to be done when all plugins are ready.\n editor.ui.once('ready', () => {\n this.toolbarView.fillFromConfig(this._balloonConfig, this.editor.ui.componentFactory);\n });\n }\n /**\n * Creates the toolbar view instance.\n */\n _createToolbarView() {\n const t = this.editor.locale.t;\n const shouldGroupWhenFull = !this._balloonConfig.shouldNotGroupWhenFull;\n const toolbarView = new ToolbarView(this.editor.locale, {\n shouldGroupWhenFull,\n isFloating: true\n });\n toolbarView.ariaLabel = t('Editor contextual toolbar');\n toolbarView.render();\n return toolbarView;\n }\n /**\n * Shows the toolbar and attaches it to the selection.\n *\n * Fires {@link #event:show} event which can be stopped to prevent the toolbar from showing up.\n *\n * @param showForCollapsedSelection When set `true`, the toolbar will show despite collapsed selection in the\n * editing view.\n */\n show(showForCollapsedSelection = false) {\n const editor = this.editor;\n const selection = editor.model.document.selection;\n const schema = editor.model.schema;\n // Do not add the toolbar to the balloon stack twice.\n if (this._balloon.hasView(this.toolbarView)) {\n return;\n }\n // Do not show the toolbar when the selection is collapsed.\n if (selection.isCollapsed && !showForCollapsedSelection) {\n return;\n }\n // Do not show the toolbar when there is more than one range in the selection and they fully contain selectable elements.\n // See https://github.com/ckeditor/ckeditor5/issues/6443.\n if (selectionContainsOnlyMultipleSelectables(selection, schema)) {\n return;\n }\n // Don not show the toolbar when all components inside are disabled\n // see https://github.com/ckeditor/ckeditor5-ui/issues/269.\n if (Array.from(this.toolbarView.items).every((item) => item.isEnabled !== undefined && !item.isEnabled)) {\n return;\n }\n // Update the toolbar position when the editor ui should be refreshed.\n this.listenTo(this.editor.ui, 'update', () => {\n this._updatePosition();\n });\n // Add the toolbar to the common editor contextual balloon.\n this._balloon.add({\n view: this.toolbarView,\n position: this._getBalloonPositionData(),\n balloonClassName: 'ck-toolbar-container'\n });\n }\n /**\n * Hides the toolbar.\n */\n hide() {\n if (this._balloon.hasView(this.toolbarView)) {\n this.stopListening(this.editor.ui, 'update');\n this._balloon.remove(this.toolbarView);\n }\n }\n /**\n * Returns positioning options for the {@link #_balloon}. They control the way balloon is attached\n * to the selection.\n */\n _getBalloonPositionData() {\n const editor = this.editor;\n const view = editor.editing.view;\n const viewDocument = view.document;\n const viewSelection = viewDocument.selection;\n // Get direction of the selection.\n const isBackward = viewDocument.selection.isBackward;\n return {\n // Because the target for BalloonPanelView is a Rect (not DOMRange), it's geometry will stay fixed\n // as the window scrolls. To let the BalloonPanelView follow such Rect, is must be continuously\n // computed and hence, the target is defined as a function instead of a static value.\n // https://github.com/ckeditor/ckeditor5-ui/issues/195\n target: () => {\n const range = isBackward ? viewSelection.getFirstRange() : viewSelection.getLastRange();\n const rangeRects = Rect.getDomRangeRects(view.domConverter.viewRangeToDom(range));\n // Select the proper range rect depending on the direction of the selection.\n if (isBackward) {\n return rangeRects[0];\n }\n else {\n // Ditch the zero-width \"orphan\" rect in the next line for the forward selection if there's\n // another one preceding it. It is not rendered as a selection by the web browser anyway.\n // https://github.com/ckeditor/ckeditor5-ui/issues/308\n if (rangeRects.length > 1 && rangeRects[rangeRects.length - 1].width === 0) {\n rangeRects.pop();\n }\n return rangeRects[rangeRects.length - 1];\n }\n },\n positions: this._getBalloonPositions(isBackward)\n };\n }\n /**\n * Updates the position of the {@link #_balloon} to make up for changes:\n *\n * * in the geometry of the selection it is attached to (e.g. the selection moved in the viewport or expanded or shrunk),\n * * or the geometry of the balloon toolbar itself (e.g. the toolbar has grouped or ungrouped some items and it is shorter or longer).\n */\n _updatePosition() {\n this._balloon.updatePosition(this._getBalloonPositionData());\n }\n /**\n * @inheritDoc\n */\n destroy() {\n super.destroy();\n this.stopListening();\n this._fireSelectionChangeDebounced.cancel();\n this.toolbarView.destroy();\n this.focusTracker.destroy();\n if (this._resizeObserver) {\n this._resizeObserver.destroy();\n }\n }\n /**\n * Returns toolbar positions for the given direction of the selection.\n */\n _getBalloonPositions(isBackward) {\n const isSafariIniOS = env.isSafari && env.isiOS;\n // https://github.com/ckeditor/ckeditor5/issues/7707\n const positions = isSafariIniOS ? generatePositions({\n // 20px when zoomed out. Less then 20px when zoomed in; the \"radius\" of the native selection handle gets\n // smaller as the user zooms in. No less than the default v-offset, though.\n heightOffset: Math.max(BalloonPanelView.arrowHeightOffset, Math.round(20 / global.window.visualViewport.scale))\n }) : BalloonPanelView.defaultPositions;\n return isBackward ? [\n positions.northWestArrowSouth,\n positions.northWestArrowSouthWest,\n positions.northWestArrowSouthEast,\n positions.northWestArrowSouthMiddleEast,\n positions.northWestArrowSouthMiddleWest,\n positions.southWestArrowNorth,\n positions.southWestArrowNorthWest,\n positions.southWestArrowNorthEast,\n positions.southWestArrowNorthMiddleWest,\n positions.southWestArrowNorthMiddleEast\n ] : [\n positions.southEastArrowNorth,\n positions.southEastArrowNorthEast,\n positions.southEastArrowNorthWest,\n positions.southEastArrowNorthMiddleEast,\n positions.southEastArrowNorthMiddleWest,\n positions.northEastArrowSouth,\n positions.northEastArrowSouthEast,\n positions.northEastArrowSouthWest,\n positions.northEastArrowSouthMiddleEast,\n positions.northEastArrowSouthMiddleWest\n ];\n }\n}\n/**\n * Returns \"true\" when the selection has multiple ranges and each range contains a selectable element\n * and nothing else.\n */\nfunction selectionContainsOnlyMultipleSelectables(selection, schema) {\n // It doesn't contain multiple objects if there is only one range.\n if (selection.rangeCount === 1) {\n return false;\n }\n return [...selection.getRanges()].every(range => {\n const element = range.getContainedElement();\n return element && schema.isSelectable(element);\n });\n}\n","import api from \"!../../../../../style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../../css-loader/dist/cjs.js!../../../../../postcss-loader/dist/cjs.js??ruleSet[1].rules[2].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 || {};","/**\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 * @module ui/toolbar/block/blockbuttonview\n */\nimport ButtonView from '../../button/buttonview.js';\nimport { toUnit } from '@ckeditor/ckeditor5-utils';\nimport '../../../theme/components/toolbar/blocktoolbar.css';\nconst toPx = toUnit('px');\n/**\n * The block button view class.\n *\n * This view represents a button attached next to block element where the selection is anchored.\n *\n * See {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar}.\n */\nexport default class BlockButtonView extends ButtonView {\n /**\n * @inheritDoc\n */\n constructor(locale) {\n super(locale);\n const bind = this.bindTemplate;\n // Hide button on init.\n this.isVisible = false;\n this.isToggleable = true;\n this.set('top', 0);\n this.set('left', 0);\n this.extendTemplate({\n attributes: {\n class: 'ck-block-toolbar-button',\n style: {\n top: bind.to('top', val => toPx(val)),\n left: bind.to('left', val => toPx(val))\n }\n }\n });\n }\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 * @module ui/toolbar/block/blocktoolbar\n */\n/* global window */\nimport { Plugin } from '@ckeditor/ckeditor5-core';\nimport { Rect, ResizeObserver, toUnit } from '@ckeditor/ckeditor5-utils';\nimport BlockButtonView from './blockbuttonview.js';\nimport BalloonPanelView from '../../panel/balloon/balloonpanelview.js';\nimport ToolbarView, { NESTED_TOOLBAR_ICONS } from '../toolbarview.js';\nimport clickOutsideHandler from '../../bindings/clickoutsidehandler.js';\nimport normalizeToolbarConfig from '../normalizetoolbarconfig.js';\nconst toPx = toUnit('px');\n/**\n * The block toolbar plugin.\n *\n * This plugin provides a button positioned next to the block of content where the selection is anchored.\n * Upon clicking the button, a dropdown providing access to editor features shows up, as configured in\n * {@link module:core/editor/editorconfig~EditorConfig#blockToolbar}.\n *\n * By default, the button is displayed next to all elements marked in {@link module:engine/model/schema~Schema}\n * as `$block` for which the toolbar provides at least one option.\n *\n * By default, the button is attached so its right boundary is touching the\n * {@link module:engine/view/editableelement~EditableElement}:\n *\n * ```\n * __ |\n * | || This is a block of content that the\n * ¯¯ | button is attached to. This is a\n * | block of content that the button is\n * | attached to.\n * ```\n *\n * The position of the button can be adjusted using the CSS `transform` property:\n *\n * ```css\n * .ck-block-toolbar-button {\n * \ttransform: translateX( -10px );\n * }\n * ```\n *\n * ```\n * __ |\n * | | | This is a block of content that the\n * ¯¯ | button is attached to. This is a\n * | block of content that the button is\n * | attached to.\n * ```\n *\n * **Note**: If you plan to run the editor in a right–to–left (RTL) language, keep in mind the button\n * will be attached to the **right** boundary of the editable area. In that case, make sure the\n * CSS position adjustment works properly by adding the following styles:\n *\n * ```css\n * .ck[dir=\"rtl\"] .ck-block-toolbar-button {\n * \ttransform: translateX( 10px );\n * }\n * ```\n */\nexport default class BlockToolbar extends Plugin {\n /**\n * @inheritDoc\n */\n static get pluginName() {\n return 'BlockToolbar';\n }\n /**\n * @inheritDoc\n */\n constructor(editor) {\n super(editor);\n /**\n * An instance of the resize observer that allows to respond to changes in editable's geometry\n * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).\n *\n * **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the\n * {@link module:core/editor/editorconfig~EditorConfig#blockToolbar configuration}.\n */\n this._resizeObserver = null;\n this._blockToolbarConfig = normalizeToolbarConfig(this.editor.config.get('blockToolbar'));\n this.toolbarView = this._createToolbarView();\n this.panelView = this._createPanelView();\n this.buttonView = this._createButtonView();\n // Close the #panelView upon clicking outside of the plugin UI.\n clickOutsideHandler({\n emitter: this.panelView,\n contextElements: [this.panelView.element, this.buttonView.element],\n activator: () => this.panelView.isVisible,\n callback: () => this._hidePanel()\n });\n }\n /**\n * @inheritDoc\n */\n init() {\n const editor = this.editor;\n const t = editor.t;\n const editBlockText = t('Click to edit block');\n const dragToMoveText = t('Drag to move');\n const editBlockLabel = t('Edit block');\n const isDragDropBlockToolbarPluginLoaded = editor.plugins.has('DragDropBlockToolbar');\n const label = isDragDropBlockToolbarPluginLoaded ? `${editBlockText}\\n${dragToMoveText}` : editBlockLabel;\n this.buttonView.label = label;\n if (isDragDropBlockToolbarPluginLoaded) {\n this.buttonView.element.dataset.ckeTooltipClass = 'ck-tooltip_multi-line';\n }\n // Hides panel on a direct selection change.\n this.listenTo(editor.model.document.selection, 'change:range', (evt, data) => {\n if (data.directChange) {\n this._hidePanel();\n }\n });\n this.listenTo(editor.ui, 'update', () => this._updateButton());\n // `low` priority is used because of https://github.com/ckeditor/ckeditor5-core/issues/133.\n this.listenTo(editor, 'change:isReadOnly', () => this._updateButton(), { priority: 'low' });\n this.listenTo(editor.ui.focusTracker, 'change:isFocused', () => this._updateButton());\n // Reposition button on resize.\n this.listenTo(this.buttonView, 'change:isVisible', (evt, name, isVisible) => {\n if (isVisible) {\n // Keep correct position of button and panel on window#resize.\n this.buttonView.listenTo(window, 'resize', () => this._updateButton());\n }\n else {\n // Stop repositioning button when is hidden.\n this.buttonView.stopListening(window, 'resize');\n // Hide the panel when the button disappears.\n this._hidePanel();\n }\n });\n // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.\n editor.ui.addToolbar(this.toolbarView, {\n beforeFocus: () => this._showPanel(),\n afterBlur: () => this._hidePanel()\n });\n // Fills the toolbar with its items based on the configuration.\n // This needs to be done after all plugins are ready.\n editor.ui.once('ready', () => {\n this.toolbarView.fillFromConfig(this._blockToolbarConfig, this.editor.ui.componentFactory);\n // Hide panel before executing each button in the panel.\n for (const item of this.toolbarView.items) {\n item.on('execute', () => this._hidePanel(true), { priority: 'high' });\n }\n });\n }\n /**\n * @inheritDoc\n */\n destroy() {\n super.destroy();\n // Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).\n this.panelView.destroy();\n this.buttonView.destroy();\n this.toolbarView.destroy();\n if (this._resizeObserver) {\n this._resizeObserver.destroy();\n }\n }\n /**\n * Creates the {@link #toolbarView}.\n */\n _createToolbarView() {\n const t = this.editor.locale.t;\n const shouldGroupWhenFull = !this._blockToolbarConfig.shouldNotGroupWhenFull;\n const toolbarView = new ToolbarView(this.editor.locale, {\n shouldGroupWhenFull,\n isFloating: true\n });\n toolbarView.ariaLabel = t('Editor block content toolbar');\n return toolbarView;\n }\n /**\n * Creates the {@link #panelView}.\n */\n _createPanelView() {\n const editor = this.editor;\n const panelView = new BalloonPanelView(editor.locale);\n panelView.content.add(this.toolbarView);\n panelView.class = 'ck-toolbar-container';\n editor.ui.view.body.add(panelView);\n editor.ui.focusTracker.add(panelView.element);\n // Close #panelView on `Esc` press.\n this.toolbarView.keystrokes.set('Esc', (evt, cancel) => {\n this._hidePanel(true);\n cancel();\n });\n return panelView;\n }\n /**\n * Creates the {@link #buttonView}.\n */\n _createButtonView() {\n const editor = this.editor;\n const t = editor.t;\n const buttonView = new BlockButtonView(editor.locale);\n const iconFromConfig = this._blockToolbarConfig.icon;\n const icon = NESTED_TOOLBAR_ICONS[iconFromConfig] || iconFromConfig || NESTED_TOOLBAR_ICONS.dragIndicator;\n buttonView.set({\n label: t('Edit block'),\n icon,\n withText: false\n });\n // Bind the panelView observable properties to the buttonView.\n buttonView.bind('isOn').to(this.panelView, 'isVisible');\n buttonView.bind('tooltip').to(this.panelView, 'isVisible', isVisible => !isVisible);\n // Toggle the panelView upon buttonView#execute.\n this.listenTo(buttonView, 'execute', () => {\n if (!this.panelView.isVisible) {\n this._showPanel();\n }\n else {\n this._hidePanel(true);\n }\n });\n editor.ui.view.body.add(buttonView);\n editor.ui.focusTracker.add(buttonView.element);\n return buttonView;\n }\n /**\n * Shows or hides the button.\n * When all the conditions for displaying the button are matched, it shows the button. Hides otherwise.\n */\n _updateButton() {\n const editor = this.editor;\n const model = editor.model;\n const view = editor.editing.view;\n // Hides the button when the editor is not focused.\n if (!editor.ui.focusTracker.isFocused) {\n this._hideButton();\n return;\n }\n // Hides the button when the selection is in non-editable place.\n if (!editor.model.canEditAt(editor.model.document.selection)) {\n this._hideButton();\n return;\n }\n // Get the first selected block, button will be attached to this element.\n const modelTarget = Array.from(model.document.selection.getSelectedBlocks())[0];\n // Hides the button when there is no enabled item in toolbar for the current block element.\n if (!modelTarget || Array.from(this.toolbarView.items).every((item) => !item.isEnabled)) {\n this._hideButton();\n return;\n }\n // Get DOM target element.\n const domTarget = view.domConverter.mapViewToDom(editor.editing.mapper.toViewElement(modelTarget));\n // Show block button.\n this.buttonView.isVisible = true;\n // Make sure that the block toolbar panel is resized properly.\n this._setupToolbarResize();\n // Attach block button to target DOM element.\n this._attachButtonToElement(domTarget);\n // When panel is opened then refresh it position to be properly aligned with block button.\n if (this.panelView.isVisible) {\n this._showPanel();\n }\n }\n /**\n * Hides the button.\n */\n _hideButton() {\n this.buttonView.isVisible = false;\n }\n /**\n * Shows the {@link #toolbarView} attached to the {@link #buttonView}.\n * If the toolbar is already visible, then it simply repositions it.\n */\n _showPanel() {\n // Usually, the only way to show the toolbar is by pressing the block button. It makes it impossible for\n // the toolbar to show up when the button is invisible (feature does not make sense for the selection then).\n // The toolbar navigation using Alt+F10 does not access the button but shows the panel directly using this method.\n // So we need to check whether this is possible first.\n if (!this.buttonView.isVisible) {\n return;\n }\n const wasVisible = this.panelView.isVisible;\n // So here's the thing: If there was no initial panelView#show() or these two were in different order, the toolbar\n // positioning will break in RTL editors. Weird, right? What you show know is that the toolbar\n // grouping works thanks to:\n //\n // * the ResizeObserver, which kicks in as soon as the toolbar shows up in DOM (becomes visible again).\n // * the observable ToolbarView#maxWidth, which triggers re-grouping when changed.\n //\n // Here are the possible scenarios:\n //\n // 1. (WRONG ❌) If the #maxWidth is set when the toolbar is invisible, it won't affect item grouping (no DOMRects, no grouping).\n // Then, when panelView.pin() is called, the position of the toolbar will be calculated for the old\n // items grouping state, and when finally ResizeObserver kicks in (hey, the toolbar is visible now, right?)\n // it will group/ungroup some items and the length of the toolbar will change. But since in RTL the toolbar\n // is attached on the right side and the positioning uses CSS \"left\", it will result in the toolbar shifting\n // to the left and being displayed in the wrong place.\n // 2. (WRONG ❌) If the panelView.pin() is called first and #maxWidth set next, then basically the story repeats. The balloon\n // calculates the position for the old toolbar grouping state, then the toolbar re-groups items and because\n // it is positioned using CSS \"left\" it will move.\n // 3. (RIGHT ✅) We show the panel first (the toolbar does re-grouping but it does not matter), then the #maxWidth\n // is set allowing the toolbar to re-group again and finally panelView.pin() does the positioning when the\n // items grouping state is stable and final.\n //\n // https://github.com/ckeditor/ckeditor5/issues/6449, https://github.com/ckeditor/ckeditor5/issues/6575\n this.panelView.show();\n const editableElement = this._getSelectedEditableElement();\n this.toolbarView.maxWidth = this._getToolbarMaxWidth(editableElement);\n this.panelView.pin({\n target: this.buttonView.element,\n limiter: editableElement\n });\n if (!wasVisible) {\n this.toolbarView.items.get(0).focus();\n }\n }\n /**\n * Returns currently selected editable, based on the model selection.\n */\n _getSelectedEditableElement() {\n const selectedModelRootName = this.editor.model.document.selection.getFirstRange().root.rootName;\n return this.editor.ui.getEditableElement(selectedModelRootName);\n }\n /**\n * Hides the {@link #toolbarView}.\n *\n * @param focusEditable When `true`, the editable will be focused after hiding the panel.\n */\n _hidePanel(focusEditable) {\n this.panelView.isVisible = false;\n if (focusEditable) {\n this.editor.editing.view.focus();\n }\n }\n /**\n * Attaches the {@link #buttonView} to the target block of content.\n *\n * @param targetElement Target element.\n */\n _attachButtonToElement(targetElement) {\n const contentStyles = window.getComputedStyle(targetElement);\n const editableRect = new Rect(this._getSelectedEditableElement());\n const contentPaddingTop = parseInt(contentStyles.paddingTop, 10);\n // When line height is not an integer then treat it as \"normal\".\n // MDN says that 'normal' == ~1.2 on desktop browsers.\n const contentLineHeight = parseInt(contentStyles.lineHeight, 10) || parseInt(contentStyles.fontSize, 10) * 1.2;\n const buttonRect = new Rect(this.buttonView.element);\n const contentRect = new Rect(targetElement);\n let positionLeft;\n if (this.editor.locale.uiLanguageDirection === 'ltr') {\n positionLeft = editableRect.left - buttonRect.width;\n }\n else {\n positionLeft = editableRect.right;\n }\n const positionTop = contentRect.top + contentPaddingTop + (contentLineHeight - buttonRect.height) / 2;\n buttonRect.moveTo(positionLeft, positionTop);\n const absoluteButtonRect = buttonRect.toAbsoluteRect();\n this.buttonView.top = absoluteButtonRect.top;\n this.buttonView.left = absoluteButtonRect.left;\n }\n /**\n * Creates a resize observer that observes selected editable and resizes the toolbar panel accordingly.\n */\n _setupToolbarResize() {\n const editableElement = this._getSelectedEditableElement();\n // Do this only if the automatic grouping is turned on.\n if (!this._blockToolbarConfig.shouldNotGroupWhenFull) {\n // If resize observer is attached to a different editable than currently selected editable, re-attach it.\n if (this._resizeObserver && this._resizeObserver.element !== editableElement) {\n this._resizeObserver.destroy();\n this._resizeObserver = null;\n }\n if (!this._resizeObserver) {\n this._resizeObserver = new ResizeObserver(editableElement, () => {\n this.toolbarView.maxWidth = this._getToolbarMaxWidth(editableElement);\n });\n }\n }\n }\n /**\n * Gets the {@link #toolbarView} max-width, based on given `editableElement` width plus the distance between the farthest\n * edge of the {@link #buttonView} and the editable.\n *\n * @returns A maximum width that toolbar can have, in pixels.\n */\n _getToolbarMaxWidth(editableElement) {\n const editableRect = new Rect(editableElement);\n const buttonRect = new Rect(this.buttonView.element);\n const isRTL = this.editor.locale.uiLanguageDirection === 'rtl';\n const offset = isRTL ? (buttonRect.left - editableRect.right) + buttonRect.width : editableRect.left - buttonRect.left;\n return toPx(editableRect.width + offset);\n }\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 */\nimport { EditorUI, normalizeToolbarConfig, DialogView } from 'ckeditor5/src/ui.js';\nimport { enablePlaceholder } from 'ckeditor5/src/engine.js';\nimport { ElementReplacer, Rect } from 'ckeditor5/src/utils.js';\n/**\n * The classic editor UI class.\n */\nexport default class ClassicEditorUI extends EditorUI {\n /**\n * Creates an instance of the classic editor UI class.\n *\n * @param editor The editor instance.\n * @param view The view of the UI.\n */\n constructor(editor, view) {\n super(editor);\n this.view = view;\n this._toolbarConfig = normalizeToolbarConfig(editor.config.get('toolbar'));\n this._elementReplacer = new ElementReplacer();\n this.listenTo(editor.editing.view, 'scrollToTheSelection', this._handleScrollToTheSelectionWithStickyPanel.bind(this));\n }\n /**\n * @inheritDoc\n */\n get element() {\n return this.view.element;\n }\n /**\n * Initializes the UI.\n *\n * @param replacementElement The DOM element that will be the source for the created editor.\n */\n init(replacementElement) {\n const editor = this.editor;\n const view = this.view;\n const editingView = editor.editing.view;\n const editable = view.editable;\n const editingRoot = editingView.document.getRoot();\n // The editable UI and editing root should share the same name. Then name is used\n // to recognize the particular editable, for instance in ARIA attributes.\n editable.name = editingRoot.rootName;\n view.render();\n // The editable UI element in DOM is available for sure only after the editor UI view has been rendered.\n // But it can be available earlier if a DOM element has been passed to BalloonEditor.create().\n const editableElement = editable.element;\n // Register the editable UI view in the editor. A single editor instance can aggregate multiple\n // editable areas (roots) but the classic editor has only one.\n this.setEditableElement(editable.name, editableElement);\n // Let the editable UI element respond to the changes in the global editor focus\n // tracker. It has been added to the same tracker a few lines above but, in reality, there are\n // many focusable areas in the editor, like balloons, toolbars or dropdowns and as long\n // as they have focus, the editable should act like it is focused too (although technically\n // it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user.\n // Doing otherwise will result in editable focus styles disappearing, once e.g. the\n // toolbar gets focused.\n view.editable.bind('isFocused').to(this.focusTracker);\n // Bind the editable UI element to the editing view, making it an end– and entry–point\n // of the editor's engine. This is where the engine meets the UI.\n editingView.attachDomRoot(editableElement);\n // If an element containing the initial data of the editor was provided, replace it with\n // an editor instance's UI in DOM until the editor is destroyed. For instance, a