` element, both when editing and in the editor data. The CSS class associated with the language
+ * can be used by third–party code syntax highlighters to detect and apply the correct highlighting.
+ *
+ * For instance, this language configuration:
+ *
+ * ```ts
+ * ClassicEditor
+ * .create( document.querySelector( '#editor' ), {
+ * codeBlock: {
+ * languages: [
+ * // ...
+ * { language: 'javascript', label: 'JavaScript' },
+ * // ...
+ * ]
+ * }
+ * } )
+ * .then( ... )
+ * .catch( ... );
+ * ```
+ *
+ * will result in the following structure of JavaScript code blocks in the editor editing and data:
+ *
+ * ```html
+ * window.alert( 'Hello world!' )
+ * ```
+ *
+ * You can customize the CSS class by specifying an optional `class` property in the language definition.
+ * You can set **multiple classes** but **only the first one** will be used as defining language class:
+ *
+ * ```ts
+ * ClassicEditor
+ * .create( document.querySelector( '#editor' ), {
+ * codeBlock: {
+ * languages: [
+ * // Do not render the CSS class for the plain text code blocks.
+ * { language: 'plaintext', label: 'Plain text', class: '' },
+ *
+ * // Use the "php-code" class for PHP code blocks.
+ * { language: 'php', label: 'PHP', class: 'php-code' },
+ *
+ * // Use the "js" class for JavaScript code blocks.
+ * // Note that only the first ("js") class will determine the language of the block when loading data.
+ * { language: 'javascript', label: 'JavaScript', class: 'js javascript js-code' },
+ *
+ * // Python code blocks will have the default "language-python" CSS class.
+ * { language: 'python', label: 'Python' }
+ * ]
+ * }
+ * } )
+ * .then( ... )
+ * .catch( ... );
+ * ```
+ *
+ * The default value of the language configuration is as follows:
+ *
+ * ```ts
+ * languages: [
+ * { language: 'plaintext', label: 'Plain text' }, // The default language.
+ * { language: 'c', label: 'C' },
+ * { language: 'cs', label: 'C#' },
+ * { language: 'cpp', label: 'C++' },
+ * { language: 'css', label: 'CSS' },
+ * { language: 'diff', label: 'Diff' },
+ * { language: 'html', label: 'HTML' },
+ * { language: 'java', label: 'Java' },
+ * { language: 'javascript', label: 'JavaScript' },
+ * { language: 'php', label: 'PHP' },
+ * { language: 'python', label: 'Python' },
+ * { language: 'ruby', label: 'Ruby' },
+ * { language: 'typescript', label: 'TypeScript' },
+ * { language: 'xml', label: 'XML' }
+ * ]
+ * ```
+ *
+ * **Note**: The first language defined in the configuration is considered the default one. This means it will be
+ * applied to code blocks loaded from the data that have no CSS `class` specified (or no matching `class` in the configuration).
+ * It will also be used when creating new code blocks using the main UI button. By default it is "Plain text".
+ */
+ languages?: Array;
+ /**
+ * A sequence of characters inserted or removed from the code block lines when its indentation
+ * is changed by the user, for instance, using Tab and Shift+Tab keys.
+ *
+ * The default value is a single tab character (" ", `\u0009` in Unicode).
+ *
+ * This configuration is used by `indentCodeBlock` and `outdentCodeBlock` commands (instances of
+ * {@link module:code-block/indentcodeblockcommand~IndentCodeBlockCommand}).
+ *
+ * **Note**: Setting this configuration to `false` will disable the code block indentation commands
+ * and associated keystrokes.
+ *
+ */
+ indentSequence?: string;
+}
+/**
+ * The code block language descriptor. See {@link module:code-block/codeblockconfig~CodeBlockConfig#languages} to learn more.
+ *
+ * ```ts
+ * {
+ * language: 'javascript',
+ * label: 'JavaScript'
+ * }
+ * ```
+ */
+export interface CodeBlockLanguageDefinition {
+ /**
+ * The name of the language that will be stored in the model attribute. Also, when `class`
+ * is not specified, it will be used to create the CSS class associated with the language (prefixed by "language-").
+ */
+ language: string;
+ /**
+ * The human–readable label associated with the language and displayed in the UI.
+ */
+ label: string;
+ /**
+ * The CSS class associated with the language. When not specified the `language`
+ * property is used to create a class prefixed by "language-".
+ */
+ class?: string;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockconfig.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockconfig.js
new file mode 100644
index 0000000..4fffd02
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockconfig.js
@@ -0,0 +1,5 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+export {};
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.d.ts
new file mode 100644
index 0000000..b61ef1a
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.d.ts
@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/codeblockediting
+ */
+import { Plugin, type Editor } from 'ckeditor5/src/core.js';
+import { ShiftEnter } from 'ckeditor5/src/enter.js';
+/**
+ * The editing part of the code block feature.
+ *
+ * Introduces the `'codeBlock'` command and the `'codeBlock'` model element.
+ */
+export default class CodeBlockEditing extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get pluginName(): "CodeBlockEditing";
+ /**
+ * @inheritDoc
+ */
+ static get requires(): readonly [typeof ShiftEnter];
+ /**
+ * @inheritDoc
+ */
+ constructor(editor: Editor);
+ /**
+ * @inheritDoc
+ */
+ init(): void;
+ /**
+ * @inheritDoc
+ */
+ afterInit(): void;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.js
new file mode 100644
index 0000000..b39ecc3
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockediting.js
@@ -0,0 +1,382 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/codeblockediting
+ */
+import { Plugin } from 'ckeditor5/src/core.js';
+import { ShiftEnter } from 'ckeditor5/src/enter.js';
+import { UpcastWriter } from 'ckeditor5/src/engine.js';
+import CodeBlockCommand from './codeblockcommand.js';
+import IndentCodeBlockCommand from './indentcodeblockcommand.js';
+import OutdentCodeBlockCommand from './outdentcodeblockcommand.js';
+import { getNormalizedAndLocalizedLanguageDefinitions, getLeadingWhiteSpaces, rawSnippetTextToViewDocumentFragment } from './utils.js';
+import { modelToViewCodeBlockInsertion, modelToDataViewSoftBreakInsertion, dataViewToModelCodeBlockInsertion, dataViewToModelTextNewlinesInsertion, dataViewToModelOrphanNodeConsumer } from './converters.js';
+const DEFAULT_ELEMENT = 'paragraph';
+/**
+ * The editing part of the code block feature.
+ *
+ * Introduces the `'codeBlock'` command and the `'codeBlock'` model element.
+ */
+export default class CodeBlockEditing extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get pluginName() {
+ return 'CodeBlockEditing';
+ }
+ /**
+ * @inheritDoc
+ */
+ static get requires() {
+ return [ShiftEnter];
+ }
+ /**
+ * @inheritDoc
+ */
+ constructor(editor) {
+ super(editor);
+ editor.config.define('codeBlock', {
+ languages: [
+ { language: 'plaintext', label: 'Plain text' },
+ { language: 'c', label: 'C' },
+ { language: 'cs', label: 'C#' },
+ { language: 'cpp', label: 'C++' },
+ { language: 'css', label: 'CSS' },
+ { language: 'diff', label: 'Diff' },
+ { language: 'html', label: 'HTML' },
+ { language: 'java', label: 'Java' },
+ { language: 'javascript', label: 'JavaScript' },
+ { language: 'php', label: 'PHP' },
+ { language: 'python', label: 'Python' },
+ { language: 'ruby', label: 'Ruby' },
+ { language: 'typescript', label: 'TypeScript' },
+ { language: 'xml', label: 'XML' }
+ ],
+ // A single tab.
+ indentSequence: '\t'
+ });
+ }
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+ const schema = editor.model.schema;
+ const model = editor.model;
+ const view = editor.editing.view;
+ const listEditing = editor.plugins.has('ListEditing') ?
+ editor.plugins.get('ListEditing') : null;
+ const normalizedLanguagesDefs = getNormalizedAndLocalizedLanguageDefinitions(editor);
+ // The main command.
+ editor.commands.add('codeBlock', new CodeBlockCommand(editor));
+ // Commands that change the indentation.
+ editor.commands.add('indentCodeBlock', new IndentCodeBlockCommand(editor));
+ editor.commands.add('outdentCodeBlock', new OutdentCodeBlockCommand(editor));
+ this.listenTo(view.document, 'tab', (evt, data) => {
+ const commandName = data.shiftKey ? 'outdentCodeBlock' : 'indentCodeBlock';
+ const command = editor.commands.get(commandName);
+ if (!command.isEnabled) {
+ return;
+ }
+ editor.execute(commandName);
+ data.stopPropagation();
+ data.preventDefault();
+ evt.stop();
+ }, { context: 'pre' });
+ schema.register('codeBlock', {
+ allowWhere: '$block',
+ allowChildren: '$text',
+ isBlock: true,
+ allowAttributes: ['language']
+ });
+ // Allow all list* attributes on `codeBlock` (integration with DocumentList).
+ // Disallow all attributes on $text inside `codeBlock`.
+ schema.addAttributeCheck((context, attributeName) => {
+ if (context.endsWith('codeBlock') &&
+ listEditing && listEditing.getListAttributeNames().includes(attributeName)) {
+ return true;
+ }
+ if (context.endsWith('codeBlock $text')) {
+ return false;
+ }
+ });
+ // Disallow object elements inside `codeBlock`. See #9567.
+ editor.model.schema.addChildCheck((context, childDefinition) => {
+ if (context.endsWith('codeBlock') && childDefinition.isObject) {
+ return false;
+ }
+ });
+ // Conversion.
+ editor.editing.downcastDispatcher.on('insert:codeBlock', modelToViewCodeBlockInsertion(model, normalizedLanguagesDefs, true));
+ editor.data.downcastDispatcher.on('insert:codeBlock', modelToViewCodeBlockInsertion(model, normalizedLanguagesDefs));
+ editor.data.downcastDispatcher.on('insert:softBreak', modelToDataViewSoftBreakInsertion(model), { priority: 'high' });
+ editor.data.upcastDispatcher.on('element:code', dataViewToModelCodeBlockInsertion(view, normalizedLanguagesDefs));
+ editor.data.upcastDispatcher.on('text', dataViewToModelTextNewlinesInsertion());
+ editor.data.upcastDispatcher.on('element:pre', dataViewToModelOrphanNodeConsumer(), { priority: 'high' });
+ // Intercept the clipboard input (paste) when the selection is anchored in the code block and force the clipboard
+ // data to be pasted as a single plain text. Otherwise, the code lines will split the code block and
+ // "spill out" as separate paragraphs.
+ this.listenTo(editor.editing.view.document, 'clipboardInput', (evt, data) => {
+ let insertionRange = model.createRange(model.document.selection.anchor);
+ // Use target ranges in case this is a drop.
+ if (data.targetRanges) {
+ insertionRange = editor.editing.mapper.toModelRange(data.targetRanges[0]);
+ }
+ if (!insertionRange.start.parent.is('element', 'codeBlock')) {
+ return;
+ }
+ const text = data.dataTransfer.getData('text/plain');
+ const writer = new UpcastWriter(editor.editing.view.document);
+ // Pass the view fragment to the default clipboardInput handler.
+ data.content = rawSnippetTextToViewDocumentFragment(writer, text);
+ });
+ // Make sure multi–line selection is always wrapped in a code block when `getSelectedContent()`
+ // is used (e.g. clipboard copy). Otherwise, only the raw text will be copied to the clipboard and,
+ // upon next paste, this bare text will not be inserted as a code block, which is not the best UX.
+ // Similarly, when the selection in a single line, the selected content should be an inline code
+ // so it can be pasted later on and retain it's preformatted nature.
+ this.listenTo(model, 'getSelectedContent', (evt, [selection]) => {
+ const anchor = selection.anchor;
+ if (selection.isCollapsed || !anchor.parent.is('element', 'codeBlock') || !anchor.hasSameParentAs(selection.focus)) {
+ return;
+ }
+ model.change(writer => {
+ const docFragment = evt.return;
+ // fo[o b]ar -> [o b]
+ if (anchor.parent.is('element') &&
+ (docFragment.childCount > 1 || selection.containsEntireContent(anchor.parent))) {
+ const codeBlock = writer.createElement('codeBlock', anchor.parent.getAttributes());
+ writer.append(docFragment, codeBlock);
+ const newDocumentFragment = writer.createDocumentFragment();
+ writer.append(codeBlock, newDocumentFragment);
+ evt.return = newDocumentFragment;
+ return;
+ }
+ // "f[oo]" -> <$text code="true">oo
+ const textNode = docFragment.getChild(0);
+ if (schema.checkAttribute(textNode, 'code')) {
+ writer.setAttribute('code', true, textNode);
+ }
+ });
+ });
+ }
+ /**
+ * @inheritDoc
+ */
+ afterInit() {
+ const editor = this.editor;
+ const commands = editor.commands;
+ const indent = commands.get('indent');
+ const outdent = commands.get('outdent');
+ if (indent) {
+ // Priority is highest due to integration with `IndentList` command of `List` plugin.
+ // If selection is in a code block we give priority to it. This way list item cannot be indented
+ // but if we would give priority to indenting list item then user would have to indent list item
+ // as much as possible and only then he could indent code block.
+ indent.registerChildCommand(commands.get('indentCodeBlock'), { priority: 'highest' });
+ }
+ if (outdent) {
+ outdent.registerChildCommand(commands.get('outdentCodeBlock'));
+ }
+ // Customize the response to the Enter and Shift+Enter
+ // key press when the selection is in the code block. Upon enter key press we can either
+ // leave the block if it's "two or three enters" in a row or create a new code block line, preserving
+ // previous line's indentation.
+ this.listenTo(editor.editing.view.document, 'enter', (evt, data) => {
+ const positionParent = editor.model.document.selection.getLastPosition().parent;
+ if (!positionParent.is('element', 'codeBlock')) {
+ return;
+ }
+ if (!leaveBlockStartOnEnter(editor, data.isSoft) && !leaveBlockEndOnEnter(editor, data.isSoft)) {
+ breakLineOnEnter(editor);
+ }
+ data.preventDefault();
+ evt.stop();
+ }, { context: 'pre' });
+ }
+}
+/**
+ * Normally, when the Enter (or Shift+Enter) key is pressed, a soft line break is to be added to the
+ * code block. Let's try to follow the indentation of the previous line when possible, for instance:
+ *
+ * ```html
+ * // Before pressing enter (or shift enter)
+ *
+ * " foo()"[] // Indent of 4 spaces.
+ *
+ *
+ * // After pressing:
+ *
+ * " foo()" // Indent of 4 spaces.
+ * // A new soft break created by pressing enter.
+ * " "[] // Retain the indent of 4 spaces.
+ *
+ * ```
+ */
+function breakLineOnEnter(editor) {
+ const model = editor.model;
+ const modelDoc = model.document;
+ const lastSelectionPosition = modelDoc.selection.getLastPosition();
+ const node = lastSelectionPosition.nodeBefore || lastSelectionPosition.textNode;
+ let leadingWhiteSpaces;
+ // Figure out the indentation (white space chars) at the beginning of the line.
+ if (node && node.is('$text')) {
+ leadingWhiteSpaces = getLeadingWhiteSpaces(node);
+ }
+ // Keeping everything in a change block for a single undo step.
+ editor.model.change(writer => {
+ editor.execute('shiftEnter');
+ // If the line before being broken in two had some indentation, let's retain it
+ // in the new line.
+ if (leadingWhiteSpaces) {
+ writer.insertText(leadingWhiteSpaces, modelDoc.selection.anchor);
+ }
+ });
+}
+/**
+ * Leave the code block when Enter (but NOT Shift+Enter) has been pressed twice at the beginning
+ * of the code block:
+ *
+ * ```html
+ * // Before:
+ * [] foo
+ *
+ * // After pressing:
+ * [] foo
+ * ```
+ *
+ * @param isSoftEnter When `true`, enter was pressed along with Shift.
+ * @returns `true` when selection left the block. `false` if stayed.
+ */
+function leaveBlockStartOnEnter(editor, isSoftEnter) {
+ const model = editor.model;
+ const modelDoc = model.document;
+ const view = editor.editing.view;
+ const lastSelectionPosition = modelDoc.selection.getLastPosition();
+ const nodeAfter = lastSelectionPosition.nodeAfter;
+ if (isSoftEnter || !modelDoc.selection.isCollapsed || !lastSelectionPosition.isAtStart) {
+ return false;
+ }
+ if (!isSoftBreakNode(nodeAfter)) {
+ return false;
+ }
+ // We're doing everything in a single change block to have a single undo step.
+ editor.model.change(writer => {
+ // "Clone" the in the standard way.
+ editor.execute('enter');
+ // The cloned block exists now before the original code block.
+ const newBlock = modelDoc.selection.anchor.parent.previousSibling;
+ // Make the cloned a regular (with clean attributes, so no language).
+ writer.rename(newBlock, DEFAULT_ELEMENT);
+ writer.setSelection(newBlock, 'in');
+ editor.model.schema.removeDisallowedAttributes([newBlock], writer);
+ // Remove the that originally followed the selection position.
+ writer.remove(nodeAfter);
+ });
+ // Eye candy.
+ view.scrollToTheSelection();
+ return true;
+}
+/**
+ * Leave the code block when Enter (but NOT Shift+Enter) has been pressed twice at the end
+ * of the code block:
+ *
+ * ```html
+ * // Before:
+ * foo[]
+ *
+ * // After first press:
+ * foo []
+ *
+ * // After second press:
+ * foo []
+ * ```
+ *
+ * @param isSoftEnter When `true`, enter was pressed along with Shift.
+ * @returns `true` when selection left the block. `false` if stayed.
+ */
+function leaveBlockEndOnEnter(editor, isSoftEnter) {
+ const model = editor.model;
+ const modelDoc = model.document;
+ const view = editor.editing.view;
+ const lastSelectionPosition = modelDoc.selection.getLastPosition();
+ const nodeBefore = lastSelectionPosition.nodeBefore;
+ let emptyLineRangeToRemoveOnEnter;
+ if (isSoftEnter || !modelDoc.selection.isCollapsed || !lastSelectionPosition.isAtEnd || !nodeBefore || !nodeBefore.previousSibling) {
+ return false;
+ }
+ // When the position is directly preceded by two soft breaks
+ //
+ // foo []
+ //
+ // it creates the following range that will be cleaned up before leaving:
+ //
+ // foo[ ]
+ //
+ if (isSoftBreakNode(nodeBefore) && isSoftBreakNode(nodeBefore.previousSibling)) {
+ emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling), model.createPositionAfter(nodeBefore));
+ }
+ // When there's some text before the position that is
+ // preceded by two soft breaks and made purely of white–space characters
+ //
+ // foo []
+ //
+ // it creates the following range to clean up before leaving:
+ //
+ // foo[ ]
+ //
+ else if (isEmptyishTextNode(nodeBefore) &&
+ isSoftBreakNode(nodeBefore.previousSibling) &&
+ isSoftBreakNode(nodeBefore.previousSibling.previousSibling)) {
+ emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling.previousSibling), model.createPositionAfter(nodeBefore));
+ }
+ // When there's some text before the position that is made purely of white–space characters
+ // and is preceded by some other text made purely of white–space characters
+ //
+ // foo []
+ //
+ // it creates the following range to clean up before leaving:
+ //
+ // foo[ ]
+ //
+ else if (isEmptyishTextNode(nodeBefore) &&
+ isSoftBreakNode(nodeBefore.previousSibling) &&
+ isEmptyishTextNode(nodeBefore.previousSibling.previousSibling) &&
+ nodeBefore.previousSibling.previousSibling &&
+ isSoftBreakNode(nodeBefore.previousSibling.previousSibling.previousSibling)) {
+ emptyLineRangeToRemoveOnEnter = model.createRange(model.createPositionBefore(nodeBefore.previousSibling.previousSibling.previousSibling), model.createPositionAfter(nodeBefore));
+ }
+ // Not leaving the block in the following cases:
+ //
+ // []
+ // a []
+ // foo []
+ // foo bar[]
+ // foo a []
+ //
+ else {
+ return false;
+ }
+ // We're doing everything in a single change block to have a single undo step.
+ editor.model.change(writer => {
+ // Remove the last s and all white space characters that followed them.
+ writer.remove(emptyLineRangeToRemoveOnEnter);
+ // "Clone" the in the standard way.
+ editor.execute('enter');
+ const newBlock = modelDoc.selection.anchor.parent;
+ // Make the cloned a regular (with clean attributes, so no language).
+ writer.rename(newBlock, DEFAULT_ELEMENT);
+ editor.model.schema.removeDisallowedAttributes([newBlock], writer);
+ });
+ // Eye candy.
+ view.scrollToTheSelection();
+ return true;
+}
+function isEmptyishTextNode(node) {
+ return node && node.is('$text') && !node.data.match(/\S/);
+}
+function isSoftBreakNode(node) {
+ return node && node.is('element', 'softBreak');
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.d.ts
new file mode 100644
index 0000000..e204a55
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.d.ts
@@ -0,0 +1,29 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/codeblockui
+ */
+import { Plugin } from 'ckeditor5/src/core.js';
+import '../theme/codeblock.css';
+/**
+ * The code block UI plugin.
+ *
+ * Introduces the `'codeBlock'` dropdown.
+ */
+export default class CodeBlockUI extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get pluginName(): "CodeBlockUI";
+ /**
+ * @inheritDoc
+ */
+ init(): void;
+ /**
+ * A helper returning a collection of the `codeBlock` dropdown items representing languages
+ * available for the user to choose from.
+ */
+ private _getLanguageListItemDefinitions;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.js
new file mode 100644
index 0000000..e1a2b94
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/codeblockui.js
@@ -0,0 +1,123 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/codeblockui
+ */
+import { icons, Plugin } from 'ckeditor5/src/core.js';
+import { Collection } from 'ckeditor5/src/utils.js';
+import { ViewModel, SplitButtonView, createDropdown, addListToDropdown, MenuBarMenuListItemButtonView, MenuBarMenuListView, MenuBarMenuView, MenuBarMenuListItemView } from 'ckeditor5/src/ui.js';
+import { getNormalizedAndLocalizedLanguageDefinitions } from './utils.js';
+import '../theme/codeblock.css';
+/**
+ * The code block UI plugin.
+ *
+ * Introduces the `'codeBlock'` dropdown.
+ */
+export default class CodeBlockUI extends Plugin {
+ /**
+ * @inheritDoc
+ */
+ static get pluginName() {
+ return 'CodeBlockUI';
+ }
+ /**
+ * @inheritDoc
+ */
+ init() {
+ const editor = this.editor;
+ const t = editor.t;
+ const componentFactory = editor.ui.componentFactory;
+ const normalizedLanguageDefs = getNormalizedAndLocalizedLanguageDefinitions(editor);
+ const itemDefinitions = this._getLanguageListItemDefinitions(normalizedLanguageDefs);
+ const command = editor.commands.get('codeBlock');
+ componentFactory.add('codeBlock', locale => {
+ const dropdownView = createDropdown(locale, SplitButtonView);
+ const splitButtonView = dropdownView.buttonView;
+ const accessibleLabel = t('Insert code block');
+ splitButtonView.set({
+ label: accessibleLabel,
+ tooltip: true,
+ icon: icons.codeBlock,
+ isToggleable: true
+ });
+ splitButtonView.bind('isOn').to(command, 'value', value => !!value);
+ splitButtonView.on('execute', () => {
+ editor.execute('codeBlock', {
+ usePreviousLanguageChoice: true
+ });
+ editor.editing.view.focus();
+ });
+ dropdownView.on('execute', evt => {
+ editor.execute('codeBlock', {
+ language: evt.source._codeBlockLanguage,
+ forceValue: true
+ });
+ editor.editing.view.focus();
+ });
+ dropdownView.class = 'ck-code-block-dropdown';
+ dropdownView.bind('isEnabled').to(command);
+ addListToDropdown(dropdownView, itemDefinitions, {
+ role: 'menu',
+ ariaLabel: accessibleLabel
+ });
+ return dropdownView;
+ });
+ componentFactory.add('menuBar:codeBlock', locale => {
+ const menuView = new MenuBarMenuView(locale);
+ menuView.buttonView.set({
+ label: t('Code block'),
+ icon: icons.codeBlock
+ });
+ menuView.bind('isEnabled').to(command);
+ const listView = new MenuBarMenuListView(locale);
+ listView.set({
+ ariaLabel: t('Insert code block')
+ });
+ for (const definition of itemDefinitions) {
+ const listItemView = new MenuBarMenuListItemView(locale, menuView);
+ const buttonView = new MenuBarMenuListItemButtonView(locale);
+ buttonView.bind(...Object.keys(definition.model)).to(definition.model);
+ buttonView.bind('ariaChecked').to(buttonView, 'isOn');
+ buttonView.delegate('execute').to(menuView);
+ buttonView.on('execute', () => {
+ editor.execute('codeBlock', {
+ language: definition.model._codeBlockLanguage,
+ forceValue: command.value == definition.model._codeBlockLanguage ? false : true
+ });
+ editor.editing.view.focus();
+ });
+ listItemView.children.add(buttonView);
+ listView.items.add(listItemView);
+ }
+ menuView.panelView.children.add(listView);
+ return menuView;
+ });
+ }
+ /**
+ * A helper returning a collection of the `codeBlock` dropdown items representing languages
+ * available for the user to choose from.
+ */
+ _getLanguageListItemDefinitions(normalizedLanguageDefs) {
+ const editor = this.editor;
+ const command = editor.commands.get('codeBlock');
+ const itemDefinitions = new Collection();
+ for (const languageDef of normalizedLanguageDefs) {
+ const definition = {
+ type: 'button',
+ model: new ViewModel({
+ _codeBlockLanguage: languageDef.language,
+ label: languageDef.label,
+ role: 'menuitemradio',
+ withText: true
+ })
+ };
+ definition.model.bind('isOn').to(command, 'value', value => {
+ return value === definition.model._codeBlockLanguage;
+ });
+ itemDefinitions.add(definition);
+ }
+ return itemDefinitions;
+ }
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.d.ts
new file mode 100644
index 0000000..9e16462
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.d.ts
@@ -0,0 +1,126 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/converters
+ */
+import type { GetCallback } from 'ckeditor5/src/utils.js';
+import type { DowncastInsertEvent, Model, UpcastElementEvent, UpcastTextEvent, EditingView } from 'ckeditor5/src/engine.js';
+import type { CodeBlockLanguageDefinition } from './codeblockconfig.js';
+/**
+ * A model-to-view (both editing and data) converter for the `codeBlock` element.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * Sample output (editing):
+ *
+ * ```html
+ * foo();
bar();
+ * ```
+ *
+ * Sample output (data, see {@link module:code-block/converters~modelToDataViewSoftBreakInsertion}):
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * @param languageDefs The normalized language configuration passed to the feature.
+ * @param useLabels When `true`, the `` element will get a `data-language` attribute with a
+ * human–readable label of the language. Used only in the editing.
+ * @returns Returns a conversion callback.
+ */
+export declare function modelToViewCodeBlockInsertion(model: Model, languageDefs: Array, useLabels?: boolean): GetCallback;
+/**
+ * A model-to-data view converter for the new line (`softBreak`) separator.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * @returns Returns a conversion callback.
+ */
+export declare function modelToDataViewSoftBreakInsertion(model: Model): GetCallback;
+/**
+ * A view-to-model converter for `` with the `` HTML.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo();bar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo();bar();
+ * ```
+ *
+ * @param languageDefs The normalized language configuration passed to the feature.
+ * @returns Returns a conversion callback.
+ */
+export declare function dataViewToModelCodeBlockInsertion(editingView: EditingView, languageDefs: Array): GetCallback;
+/**
+ * A view-to-model converter for new line characters in ``.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * @returns {Function} Returns a conversion callback.
+ */
+export declare function dataViewToModelTextNewlinesInsertion(): GetCallback;
+/**
+ * A view-to-model converter that handles orphan text nodes (white spaces, new lines, etc.)
+ * that surround `` inside ``.
+ *
+ * Sample input:
+ *
+ * ```html
+ * // White spaces
+ * foo()
+ *
+ * // White spaces
+ * foo()
+ *
+ * // White spaces
+ * foo()
+ *
+ * // New lines
+ *
+ * foo()
+ *
+ *
+ * // Redundant text
+ * ABCfoo()DEF
+ * ```
+ *
+ * Unified output for each case:
+ *
+ * ```html
+ * foo()
+ * ```
+ *
+ * @returns Returns a conversion callback.
+ */
+export declare function dataViewToModelOrphanNodeConsumer(): GetCallback;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.js
new file mode 100644
index 0000000..ecf642e
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/converters.js
@@ -0,0 +1,277 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+import { getPropertyAssociation } from './utils.js';
+/**
+ * A model-to-view (both editing and data) converter for the `codeBlock` element.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * Sample output (editing):
+ *
+ * ```html
+ * foo();
bar();
+ * ```
+ *
+ * Sample output (data, see {@link module:code-block/converters~modelToDataViewSoftBreakInsertion}):
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * @param languageDefs The normalized language configuration passed to the feature.
+ * @param useLabels When `true`, the `` element will get a `data-language` attribute with a
+ * human–readable label of the language. Used only in the editing.
+ * @returns Returns a conversion callback.
+ */
+export function modelToViewCodeBlockInsertion(model, languageDefs, useLabels = false) {
+ // Language CSS classes:
+ //
+ // {
+ // php: 'language-php',
+ // python: 'language-python',
+ // javascript: 'js',
+ // ...
+ // }
+ const languagesToClasses = getPropertyAssociation(languageDefs, 'language', 'class');
+ // Language labels:
+ //
+ // {
+ // php: 'PHP',
+ // python: 'Python',
+ // javascript: 'JavaScript',
+ // ...
+ // }
+ const languagesToLabels = getPropertyAssociation(languageDefs, 'language', 'label');
+ return (evt, data, conversionApi) => {
+ const { writer, mapper, consumable } = conversionApi;
+ if (!consumable.consume(data.item, 'insert')) {
+ return;
+ }
+ const codeBlockLanguage = data.item.getAttribute('language');
+ const targetViewPosition = mapper.toViewPosition(model.createPositionBefore(data.item));
+ const preAttributes = {};
+ // Attributes added only in the editing view.
+ if (useLabels) {
+ preAttributes['data-language'] = languagesToLabels[codeBlockLanguage];
+ preAttributes.spellcheck = 'false';
+ }
+ const codeAttributes = languagesToClasses[codeBlockLanguage] ? {
+ class: languagesToClasses[codeBlockLanguage]
+ } : undefined;
+ const code = writer.createContainerElement('code', codeAttributes);
+ const pre = writer.createContainerElement('pre', preAttributes, code);
+ writer.insert(targetViewPosition, pre);
+ mapper.bindElements(data.item, code);
+ };
+}
+/**
+ * A model-to-data view converter for the new line (`softBreak`) separator.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * @returns Returns a conversion callback.
+ */
+export function modelToDataViewSoftBreakInsertion(model) {
+ return (evt, data, conversionApi) => {
+ if (data.item.parent.name !== 'codeBlock') {
+ return;
+ }
+ const { writer, mapper, consumable } = conversionApi;
+ if (!consumable.consume(data.item, 'insert')) {
+ return;
+ }
+ const position = mapper.toViewPosition(model.createPositionBefore(data.item));
+ writer.insert(position, writer.createText('\n'));
+ };
+}
+/**
+ * A view-to-model converter for `` with the `` HTML.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo();bar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo();bar();
+ * ```
+ *
+ * @param languageDefs The normalized language configuration passed to the feature.
+ * @returns Returns a conversion callback.
+ */
+export function dataViewToModelCodeBlockInsertion(editingView, languageDefs) {
+ // Language names associated with CSS classes:
+ //
+ // {
+ // 'language-php': 'php',
+ // 'language-python': 'python',
+ // js: 'javascript',
+ // ...
+ // }
+ const classesToLanguages = getPropertyAssociation(languageDefs, 'class', 'language');
+ const defaultLanguageName = languageDefs[0].language;
+ return (evt, data, conversionApi) => {
+ const viewCodeElement = data.viewItem;
+ const viewPreElement = viewCodeElement.parent;
+ if (!viewPreElement || !viewPreElement.is('element', 'pre')) {
+ return;
+ }
+ // In case of nested code blocks we don't want to convert to another code block.
+ if (data.modelCursor.findAncestor('codeBlock')) {
+ return;
+ }
+ const { consumable, writer } = conversionApi;
+ if (!consumable.test(viewCodeElement, { name: true })) {
+ return;
+ }
+ const codeBlock = writer.createElement('codeBlock');
+ const viewChildClasses = [...viewCodeElement.getClassNames()];
+ // As we're to associate each class with a model language, a lack of class (empty class) can be
+ // also associated with a language if the language definition was configured so. Pushing an empty
+ // string to make sure the association will work.
+ if (!viewChildClasses.length) {
+ viewChildClasses.push('');
+ }
+ // Figure out if any of the element's class names is a valid programming
+ // language class. If so, use it on the model element (becomes the language of the entire block).
+ for (const className of viewChildClasses) {
+ const language = classesToLanguages[className];
+ if (language) {
+ writer.setAttribute('language', language, codeBlock);
+ break;
+ }
+ }
+ // If no language value was set, use the default language from the config.
+ if (!codeBlock.hasAttribute('language')) {
+ writer.setAttribute('language', defaultLanguageName, codeBlock);
+ }
+ conversionApi.convertChildren(viewCodeElement, codeBlock);
+ // Let's try to insert code block.
+ if (!conversionApi.safeInsert(codeBlock, data.modelCursor)) {
+ return;
+ }
+ consumable.consume(viewCodeElement, { name: true });
+ conversionApi.updateConversionResult(codeBlock, data);
+ };
+}
+/**
+ * A view-to-model converter for new line characters in ``.
+ *
+ * Sample input:
+ *
+ * ```html
+ * foo();\nbar();
+ * ```
+ *
+ * Sample output:
+ *
+ * ```html
+ * foo(); bar();
+ * ```
+ *
+ * @returns {Function} Returns a conversion callback.
+ */
+export function dataViewToModelTextNewlinesInsertion() {
+ return (evt, data, { consumable, writer }) => {
+ let position = data.modelCursor;
+ // When node is already converted then do nothing.
+ if (!consumable.test(data.viewItem)) {
+ return;
+ }
+ // When not inside `codeBlock` then do nothing.
+ if (!position.findAncestor('codeBlock')) {
+ return;
+ }
+ consumable.consume(data.viewItem);
+ const text = data.viewItem.data;
+ const textLines = text.split('\n').map(data => writer.createText(data));
+ const lastLine = textLines[textLines.length - 1];
+ for (const node of textLines) {
+ writer.insert(node, position);
+ position = position.getShiftedBy(node.offsetSize);
+ if (node !== lastLine) {
+ const softBreak = writer.createElement('softBreak');
+ writer.insert(softBreak, position);
+ position = writer.createPositionAfter(softBreak);
+ }
+ }
+ data.modelRange = writer.createRange(data.modelCursor, position);
+ data.modelCursor = position;
+ };
+}
+/**
+ * A view-to-model converter that handles orphan text nodes (white spaces, new lines, etc.)
+ * that surround `` inside ``.
+ *
+ * Sample input:
+ *
+ * ```html
+ * // White spaces
+ * foo()
+ *
+ * // White spaces
+ * foo()
+ *
+ * // White spaces
+ * foo()
+ *
+ * // New lines
+ *
+ * foo()
+ *
+ *
+ * // Redundant text
+ * ABCfoo()DEF
+ * ```
+ *
+ * Unified output for each case:
+ *
+ * ```html
+ * foo()
+ * ```
+ *
+ * @returns Returns a conversion callback.
+ */
+export function dataViewToModelOrphanNodeConsumer() {
+ return (evt, data, { consumable }) => {
+ const preElement = data.viewItem;
+ // Don't clean up nested pre elements. Their content should stay as it is, they are not upcasted
+ // to code blocks.
+ if (preElement.findAncestor('pre')) {
+ return;
+ }
+ const preChildren = Array.from(preElement.getChildren());
+ const childCodeElement = preChildren.find(node => node.is('element', 'code'));
+ // -less . It will not upcast to code block in the model, skipping.
+ if (!childCodeElement) {
+ return;
+ }
+ for (const child of preChildren) {
+ if (child === childCodeElement || !child.is('$text')) {
+ continue;
+ }
+ // Consuming the orphan to remove it from the input data.
+ // Second argument in `consumable.consume` is discarded for text nodes.
+ consumable.consume(child, { name: true });
+ }
+ };
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.d.ts
new file mode 100644
index 0000000..de1bc57
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.d.ts
@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/indentcodeblockcommand
+ */
+import { Command, type Editor } from 'ckeditor5/src/core.js';
+/**
+ * The code block indentation increase command plugin.
+ */
+export default class IndentCodeBlockCommand extends Command {
+ /**
+ * A sequence of characters added to the line when the command is executed.
+ */
+ private _indentSequence;
+ constructor(editor: Editor);
+ /**
+ * @inheritDoc
+ */
+ refresh(): void;
+ /**
+ * Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
+ * code lines in the selection will be increased.
+ *
+ * @fires execute
+ */
+ execute(): void;
+ /**
+ * Checks whether the command can be enabled in the current context.
+ */
+ private _checkEnabled;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.js
new file mode 100644
index 0000000..a8d32a2
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/indentcodeblockcommand.js
@@ -0,0 +1,78 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/indentcodeblockcommand
+ */
+import { Command } from 'ckeditor5/src/core.js';
+import { getIndentOutdentPositions, isModelSelectionInCodeBlock } from './utils.js';
+/**
+ * The code block indentation increase command plugin.
+ */
+export default class IndentCodeBlockCommand extends Command {
+ constructor(editor) {
+ super(editor);
+ this._indentSequence = editor.config.get('codeBlock.indentSequence');
+ }
+ /**
+ * @inheritDoc
+ */
+ refresh() {
+ this.isEnabled = this._checkEnabled();
+ }
+ /**
+ * Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
+ * code lines in the selection will be increased.
+ *
+ * @fires execute
+ */
+ execute() {
+ const editor = this.editor;
+ const model = editor.model;
+ model.change(writer => {
+ const positions = getIndentOutdentPositions(model);
+ // Indent all positions, for instance assuming the indent sequence is 4x space (" "):
+ //
+ // ^foo -> foo
+ //
+ // foo^bar -> foo bar
+ //
+ // Also, when there is more than one position:
+ //
+ //
+ // ^foobar
+ //
+ // ^bazqux
+ //
+ //
+ // ->
+ //
+ //
+ // foobar
+ //
+ // bazqux
+ //
+ //
+ for (const position of positions) {
+ const indentSequenceTextElement = writer.createText(this._indentSequence);
+ // Previously insertion was done by writer.insertText(). It was changed to insertContent() to enable
+ // integration of code block with track changes. It's the easiest way of integration because insertContent()
+ // is already integrated with track changes, but if it ever cause any troubles it can be reverted, however
+ // some additional work will be required in track changes integration of code block.
+ model.insertContent(indentSequenceTextElement, position);
+ }
+ });
+ }
+ /**
+ * Checks whether the command can be enabled in the current context.
+ */
+ _checkEnabled() {
+ if (!this._indentSequence) {
+ return false;
+ }
+ // Indent (forward) command is always enabled when there's any code block in the selection
+ // because you can always indent code lines.
+ return isModelSelectionInCodeBlock(this.editor.model.document.selection);
+ }
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.d.ts
new file mode 100644
index 0000000..c069715
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.d.ts
@@ -0,0 +1,15 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block
+ */
+export { default as CodeBlock } from './codeblock.js';
+export { default as CodeBlockEditing } from './codeblockediting.js';
+export { default as CodeBlockUI } from './codeblockui.js';
+export type { default as CodeBlockCommand } from './codeblockcommand.js';
+export type { default as IndentCodeBlockCommand } from './indentcodeblockcommand.js';
+export type { default as OutdentCodeBlockCommand } from './outdentcodeblockcommand.js';
+export type { CodeBlockConfig } from './codeblockconfig.js';
+import './augmentation.js';
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.js
new file mode 100644
index 0000000..53475b7
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/index.js
@@ -0,0 +1,11 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block
+ */
+export { default as CodeBlock } from './codeblock.js';
+export { default as CodeBlockEditing } from './codeblockediting.js';
+export { default as CodeBlockUI } from './codeblockui.js';
+import './augmentation.js';
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.d.ts
new file mode 100644
index 0000000..0ea89da
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.d.ts
@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+import { Command, type Editor } from 'ckeditor5/src/core.js';
+/**
+ * The code block indentation decrease command plugin.
+ */
+export default class OutdentCodeBlockCommand extends Command {
+ /**
+ * A sequence of characters removed from the line when the command is executed.
+ */
+ private readonly _indentSequence;
+ constructor(editor: Editor);
+ /**
+ * @inheritDoc
+ */
+ refresh(): void;
+ /**
+ * Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
+ * code lines in the selection will be decreased.
+ *
+ * @fires execute
+ */
+ execute(): void;
+ /**
+ * Checks whether the command can be enabled in the current context.
+ *
+ * @private
+ * @returns {Boolean} Whether the command should be enabled.
+ */
+ private _checkEnabled;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.js
new file mode 100644
index 0000000..e740d10
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/outdentcodeblockcommand.js
@@ -0,0 +1,148 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+import { Command } from 'ckeditor5/src/core.js';
+import { getLeadingWhiteSpaces, getIndentOutdentPositions, isModelSelectionInCodeBlock } from './utils.js';
+/**
+ * The code block indentation decrease command plugin.
+ */
+export default class OutdentCodeBlockCommand extends Command {
+ constructor(editor) {
+ super(editor);
+ this._indentSequence = editor.config.get('codeBlock.indentSequence');
+ }
+ /**
+ * @inheritDoc
+ */
+ refresh() {
+ this.isEnabled = this._checkEnabled();
+ }
+ /**
+ * Executes the command. When the command {@link #isEnabled is enabled}, the indentation of the
+ * code lines in the selection will be decreased.
+ *
+ * @fires execute
+ */
+ execute() {
+ const editor = this.editor;
+ const model = editor.model;
+ model.change(() => {
+ const positions = getIndentOutdentPositions(model);
+ // Outdent all positions, for instance assuming the indent sequence is 4x space (" "):
+ //
+ // ^foo -> foo
+ //
+ // ^bar -> bar
+ //
+ // Also, when there is more than one position:
+ //
+ //
+ // ^foobar
+ //
+ // ^bazqux
+ //
+ //
+ // ->
+ //
+ //
+ // foobar
+ //
+ // bazqux
+ //
+ for (const position of positions) {
+ const range = getLastOutdentableSequenceRange(model, position, this._indentSequence);
+ if (range) {
+ // Previously deletion was done by writer.remove(). It was changed to deleteContent() to enable
+ // integration of code block with track changes. It's the easiest way of integration because deleteContent()
+ // is already integrated with track changes, but if it ever cause any troubles it can be reverted, however
+ // some additional work will be required in track changes integration of code block.
+ model.deleteContent(model.createSelection(range));
+ }
+ }
+ });
+ }
+ /**
+ * Checks whether the command can be enabled in the current context.
+ *
+ * @private
+ * @returns {Boolean} Whether the command should be enabled.
+ */
+ _checkEnabled() {
+ if (!this._indentSequence) {
+ return false;
+ }
+ const model = this.editor.model;
+ if (!isModelSelectionInCodeBlock(model.document.selection)) {
+ return false;
+ }
+ // Outdent command can execute only when there is an indent character sequence
+ // in some of the lines.
+ return getIndentOutdentPositions(model).some(position => {
+ return getLastOutdentableSequenceRange(model, position, this._indentSequence);
+ });
+ }
+}
+// For a position coming from `getIndentOutdentPositions()`, it returns the range representing
+// the last occurrence of the indent sequence among the leading whitespaces of the code line the
+// position represents.
+//
+// For instance, assuming the indent sequence is 4x space (" "):
+//
+// foo^ -> null
+// foo^ bar -> null
+// ^foo -> null
+// ^foo -> [ ]foo
+// ^foo bar -> [ ]foo bar
+//
+// @param {} model
+// @param {} position
+// @param {String} sequence
+// @returns {|null}
+function getLastOutdentableSequenceRange(model, position, sequence) {
+ // Positions start before each text node (code line). Get the node corresponding to the position.
+ const nodeAtPosition = getCodeLineTextNodeAtPosition(position);
+ if (!nodeAtPosition) {
+ return null;
+ }
+ const leadingWhiteSpaces = getLeadingWhiteSpaces(nodeAtPosition);
+ const lastIndexOfSequence = leadingWhiteSpaces.lastIndexOf(sequence);
+ // For instance, assuming the indent sequence is 4x space (" "):
+ //
+ // ^foo -> null
+ //
+ if (lastIndexOfSequence + sequence.length !== leadingWhiteSpaces.length) {
+ return null;
+ }
+ // For instance, assuming the indent sequence is 4x space (" "):
+ //
+ // ^foo -> null
+ //
+ if (lastIndexOfSequence === -1) {
+ return null;
+ }
+ const { parent, startOffset } = nodeAtPosition;
+ // Create a range that contains the **last** indent sequence among the leading whitespaces
+ // of the line.
+ //
+ // For instance, assuming the indent sequence is 4x space (" "):
+ //
+ // ^foo -> [ ]foo
+ //
+ return model.createRange(model.createPositionAt(parent, startOffset + lastIndexOfSequence), model.createPositionAt(parent, startOffset + lastIndexOfSequence + sequence.length));
+}
+function getCodeLineTextNodeAtPosition(position) {
+ // Positions start before each text node (code line). Get the node corresponding to the position.
+ let nodeAtPosition = position.parent.getChild(position.index);
+ // foo^
+ // foo^ bar
+ if (!nodeAtPosition || nodeAtPosition.is('element', 'softBreak')) {
+ nodeAtPosition = position.nodeBefore;
+ }
+ // ^
+ // foo^ bar
+ if (!nodeAtPosition || nodeAtPosition.is('element', 'softBreak')) {
+ return null;
+ }
+ return nodeAtPosition;
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.d.ts b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.d.ts
new file mode 100644
index 0000000..80f2de1
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.d.ts
@@ -0,0 +1,138 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+/**
+ * @module code-block/utils
+ */
+import type { Editor } from 'ckeditor5/src/core.js';
+import type { CodeBlockLanguageDefinition } from './codeblockconfig.js';
+import type { DocumentSelection, Element, Model, Position, Schema, Text, UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
+/**
+ * Returns code block languages as defined in `config.codeBlock.languages` but processed:
+ *
+ * * To consider the editor localization, i.e. to display {@link module:code-block/codeblockconfig~CodeBlockLanguageDefinition}
+ * in the correct language. There is no way to use {@link module:utils/locale~Locale#t} when the user
+ * configuration is defined because the editor does not exist yet.
+ * * To make sure each definition has a CSS class associated with it even if not specified
+ * in the original configuration.
+ */
+export declare function getNormalizedAndLocalizedLanguageDefinitions(editor: Editor): Array;
+/**
+ * Returns an object associating certain language definition properties with others. For instance:
+ *
+ * For:
+ *
+ * ```ts
+ * const definitions = {
+ * { language: 'php', class: 'language-php', label: 'PHP' },
+ * { language: 'javascript', class: 'js', label: 'JavaScript' },
+ * };
+ *
+ * getPropertyAssociation( definitions, 'class', 'language' );
+ * ```
+ *
+ * returns:
+ *
+ * ```ts
+ * {
+ * 'language-php': 'php',
+ * 'js': 'javascript'
+ * }
+ * ```
+ *
+ * and
+ *
+ * ```ts
+ * getPropertyAssociation( definitions, 'language', 'label' );
+ * ```
+ *
+ * returns:
+ *
+ * ```ts
+ * {
+ * 'php': 'PHP',
+ * 'javascript': 'JavaScript'
+ * }
+ * ```
+ */
+export declare function getPropertyAssociation(languageDefs: Array, key: keyof CodeBlockLanguageDefinition, value: keyof CodeBlockLanguageDefinition): Record;
+/**
+ * For a given model text node, it returns white spaces that precede other characters in that node.
+ * This corresponds to the indentation part of the code block line.
+ */
+export declare function getLeadingWhiteSpaces(textNode: Text): string;
+/**
+ * For plain text containing the code (a snippet), it returns a document fragment containing
+ * view text nodes separated by `
` elements (in place of new line characters "\n"), for instance:
+ *
+ * Input:
+ *
+ * ```ts
+ * "foo()\n
+ * bar()"
+ * ```
+ *
+ * Output:
+ *
+ * ```html
+ *
+ * "foo()"
+ *
+ * "bar()"
+ *
+ * ```
+ *
+ * @param text The raw code text to be converted.
+ */
+export declare function rawSnippetTextToViewDocumentFragment(writer: UpcastWriter, text: string): ViewDocumentFragment;
+/**
+ * Returns an array of all model positions within the selection that represent code block lines.
+ *
+ * If the selection is collapsed, it returns the exact selection anchor position:
+ *
+ * ```html
+ * []foo -> ^foo
+ * foo[]bar -> foo^bar
+ * ```
+ *
+ * Otherwise, it returns positions **before** each text node belonging to all code blocks contained by the selection:
+ *
+ * ```html
+ *
+ * foo[bar ^foobar
+ * ->
+ * baz]qux ^bazqux
+ *
+ * ```
+ *
+ * It also works across other non–code blocks:
+ *
+ * ```html
+ *
+ * foo[bar ^foobar
+ *
+ * text -> text
+ *
+ * baz]qux ^bazqux
+ *
+ * ```
+ *
+ * **Note:** The positions are in reverse order so they do not get outdated when iterating over them and
+ * the writer inserts or removes elements at the same time.
+ *
+ * **Note:** The position is located after the leading white spaces in the text node.
+ */
+export declare function getIndentOutdentPositions(model: Model): Array;
+/**
+ * Checks if any of the blocks within the model selection is a code block.
+ */
+export declare function isModelSelectionInCodeBlock(selection: DocumentSelection): boolean;
+/**
+ * Checks if an {@link module:engine/model/element~Element Element} can become a code block.
+ *
+ * @param schema Model's schema.
+ * @param element The element to be checked.
+ * @returns Check result.
+ */
+export declare function canBeCodeBlock(schema: Schema, element: Element): boolean;
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.js b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.js
new file mode 100644
index 0000000..0b5035d
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/src/utils.js
@@ -0,0 +1,209 @@
+/**
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+import { first } from 'ckeditor5/src/utils.js';
+/**
+ * Returns code block languages as defined in `config.codeBlock.languages` but processed:
+ *
+ * * To consider the editor localization, i.e. to display {@link module:code-block/codeblockconfig~CodeBlockLanguageDefinition}
+ * in the correct language. There is no way to use {@link module:utils/locale~Locale#t} when the user
+ * configuration is defined because the editor does not exist yet.
+ * * To make sure each definition has a CSS class associated with it even if not specified
+ * in the original configuration.
+ */
+export function getNormalizedAndLocalizedLanguageDefinitions(editor) {
+ const t = editor.t;
+ const languageDefs = editor.config.get('codeBlock.languages');
+ for (const def of languageDefs) {
+ if (def.label === 'Plain text') {
+ def.label = t('Plain text');
+ }
+ if (def.class === undefined) {
+ def.class = `language-${def.language}`;
+ }
+ }
+ return languageDefs;
+}
+/**
+ * Returns an object associating certain language definition properties with others. For instance:
+ *
+ * For:
+ *
+ * ```ts
+ * const definitions = {
+ * { language: 'php', class: 'language-php', label: 'PHP' },
+ * { language: 'javascript', class: 'js', label: 'JavaScript' },
+ * };
+ *
+ * getPropertyAssociation( definitions, 'class', 'language' );
+ * ```
+ *
+ * returns:
+ *
+ * ```ts
+ * {
+ * 'language-php': 'php',
+ * 'js': 'javascript'
+ * }
+ * ```
+ *
+ * and
+ *
+ * ```ts
+ * getPropertyAssociation( definitions, 'language', 'label' );
+ * ```
+ *
+ * returns:
+ *
+ * ```ts
+ * {
+ * 'php': 'PHP',
+ * 'javascript': 'JavaScript'
+ * }
+ * ```
+ */
+export function getPropertyAssociation(languageDefs, key, value) {
+ const association = {};
+ for (const def of languageDefs) {
+ if (key === 'class') {
+ // Only the first class is considered.
+ const newKey = (def[key]).split(' ').shift();
+ association[newKey] = def[value];
+ }
+ else {
+ association[def[key]] = def[value];
+ }
+ }
+ return association;
+}
+/**
+ * For a given model text node, it returns white spaces that precede other characters in that node.
+ * This corresponds to the indentation part of the code block line.
+ */
+export function getLeadingWhiteSpaces(textNode) {
+ return textNode.data.match(/^(\s*)/)[0];
+}
+/**
+ * For plain text containing the code (a snippet), it returns a document fragment containing
+ * view text nodes separated by `
` elements (in place of new line characters "\n"), for instance:
+ *
+ * Input:
+ *
+ * ```ts
+ * "foo()\n
+ * bar()"
+ * ```
+ *
+ * Output:
+ *
+ * ```html
+ *
+ * "foo()"
+ *
+ * "bar()"
+ *
+ * ```
+ *
+ * @param text The raw code text to be converted.
+ */
+export function rawSnippetTextToViewDocumentFragment(writer, text) {
+ const fragment = writer.createDocumentFragment();
+ const textLines = text.split('\n');
+ const items = textLines.reduce((nodes, line, lineIndex) => {
+ nodes.push(line);
+ if (lineIndex < textLines.length - 1) {
+ nodes.push(writer.createElement('br'));
+ }
+ return nodes;
+ }, []);
+ writer.appendChild(items, fragment);
+ return fragment;
+}
+/**
+ * Returns an array of all model positions within the selection that represent code block lines.
+ *
+ * If the selection is collapsed, it returns the exact selection anchor position:
+ *
+ * ```html
+ * []foo -> ^foo
+ * foo[]bar -> foo^bar
+ * ```
+ *
+ * Otherwise, it returns positions **before** each text node belonging to all code blocks contained by the selection:
+ *
+ * ```html
+ *
+ * foo[bar ^foobar
+ * ->
+ * baz]qux ^bazqux
+ *
+ * ```
+ *
+ * It also works across other non–code blocks:
+ *
+ * ```html
+ *
+ * foo[bar ^foobar
+ *
+ * text -> text
+ *
+ * baz]qux ^bazqux
+ *
+ * ```
+ *
+ * **Note:** The positions are in reverse order so they do not get outdated when iterating over them and
+ * the writer inserts or removes elements at the same time.
+ *
+ * **Note:** The position is located after the leading white spaces in the text node.
+ */
+export function getIndentOutdentPositions(model) {
+ const selection = model.document.selection;
+ const positions = [];
+ // When the selection is collapsed, there's only one position we can indent or outdent.
+ if (selection.isCollapsed) {
+ return [selection.anchor];
+ }
+ // When the selection is NOT collapsed, collect all positions starting before text nodes
+ // (code lines) in any within the selection.
+ // Walk backward so positions we are about to collect here do not get outdated when
+ // inserting or deleting using the writer.
+ const walker = selection.getFirstRange().getWalker({
+ ignoreElementEnd: true,
+ direction: 'backward'
+ });
+ for (const { item } of walker) {
+ if (!item.is('$textProxy')) {
+ continue;
+ }
+ const { parent, startOffset } = item.textNode;
+ if (!parent.is('element', 'codeBlock')) {
+ continue;
+ }
+ const leadingWhiteSpaces = getLeadingWhiteSpaces(item.textNode);
+ // Make sure the position is after all leading whitespaces in the text node.
+ const position = model.createPositionAt(parent, startOffset + leadingWhiteSpaces.length);
+ positions.push(position);
+ }
+ return positions;
+}
+/**
+ * Checks if any of the blocks within the model selection is a code block.
+ */
+export function isModelSelectionInCodeBlock(selection) {
+ const firstBlock = first(selection.getSelectedBlocks());
+ return !!firstBlock && firstBlock.is('element', 'codeBlock');
+}
+/**
+ * Checks if an {@link module:engine/model/element~Element Element} can become a code block.
+ *
+ * @param schema Model's schema.
+ * @param element The element to be checked.
+ * @returns Check result.
+ */
+export function canBeCodeBlock(schema, element) {
+ if (element.is('rootElement') || schema.isLimit(element)) {
+ return false;
+ }
+ return schema.checkChild(element.parent, 'codeBlock');
+}
diff --git a/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/theme/codeblock.css b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/theme/codeblock.css
new file mode 100644
index 0000000..082e403
--- /dev/null
+++ b/vendor/ckeditor5/node_modules/@ckeditor/ckeditor5-code-block/theme/codeblock.css
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+.ck-content pre {
+ padding: 1em;
+ color: hsl(0, 0%, 20.8%);
+ background: hsla(0, 0%, 78%, 0.3);
+ border: 1px solid hsl(0, 0%, 77%);
+ border-radius: 2px;
+
+ /* Code block are language direction–agnostic. */
+ text-align: left;
+ direction: ltr;
+
+ tab-size: 4;
+ white-space: pre-wrap;
+
+ /* Don't inherit the style, e.g. when in a block quote. */
+ font-style: normal;
+
+ /* Don't let the code be squashed e.g. when in a table cell. */
+ min-width: 200px;
+
+ & code {
+ background: unset;
+ padding: 0;
+ border-radius: 0;
+ }
+}
+
+.ck.ck-editor__editable pre {
+ position: relative;
+
+ &[data-language]::after {
+ content: attr(data-language);
+ position: absolute;
+ }
+}